Skip to content

Commit d5202fb

Browse files
authored
Merge pull request #14575 from Automattic/vkarpov15/gh-14573
docs(typescript): clarify that setting THydratedDocumentType on schemas is necessary for correct method context
2 parents 88afc33 + e2b0e12 commit d5202fb

File tree

2 files changed

+71
-4
lines changed

2 files changed

+71
-4
lines changed

docs/typescript/schemas.md

+19-4
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ This is because Mongoose has numerous features that add paths to your schema tha
164164
## Arrays
165165

166166
When you define an array in a document interface, we recommend using vanilla JavaScript arrays, **not** Mongoose's `Types.Array` type or `Types.DocumentArray` type.
167-
Instead, use the `THydratedDocumentType` generic to define that the hydrated document type has paths of type `Types.Array` and `Types.DocumentArray`.
167+
Instead, use the `THydratedDocumentType` generic for models and schemas to define that the hydrated document type has paths of type `Types.Array` and `Types.DocumentArray`.
168168

169169
```typescript
170170
import mongoose from 'mongoose'
@@ -178,17 +178,27 @@ interface IOrder {
178178
// for fully hydrated docs returned from `findOne()`, etc.
179179
type OrderHydratedDocument = mongoose.HydratedDocument<
180180
IOrder,
181-
{ tags: mongoose.Types.DocumentArray<{ name: string }> }
181+
{ tags: mongoose.HydratedArraySubdocument<{ name: string }> }
182182
>;
183183
type OrderModelType = mongoose.Model<
184184
IOrder,
185185
{},
186186
{},
187187
{},
188-
OrderHydratedDocument
188+
OrderHydratedDocument // THydratedDocumentType
189189
>;
190190

191-
const orderSchema = new mongoose.Schema<IOrder, OrderModelType>({
191+
const orderSchema = new mongoose.Schema<
192+
IOrder,
193+
OrderModelType,
194+
{}, // methods
195+
{}, // query helpers
196+
{}, // virtuals
197+
{}, // statics
198+
mongoose.DefaultSchemaOptions, // schema options
199+
IOrder, // doctype
200+
OrderHydratedDocument // THydratedDocumentType
201+
>({
192202
tags: [{ name: { type: String, required: true } }]
193203
});
194204
const OrderModel = mongoose.model<IOrder, OrderModelType>('Order', orderSchema);
@@ -207,3 +217,8 @@ async function run() {
207217
leanDoc.tags; // Array<{ name: string }>
208218
};
209219
```
220+
221+
Use `HydratedArraySubdocument<RawDocType>` for the type of array subdocuments, and `HydratedSingleSubdocument<RawDocType>` for single subdocuments.
222+
223+
If you are not using [schema methods](../guide.html#methods), middleware, or [virtuals](../tutorials/virtuals.html), you can omit the last 7 generic parameters to `Schema()` and just define your schema using `new mongoose.Schema<IOrder, OrderModelType>(...)`.
224+
The THydratedDocumentType parameter for schemas is primarily for setting the value of `this` on methods and virtuals.

test/types/schema.test.ts

+52
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import {
2+
DefaultSchemaOptions,
3+
HydratedSingleSubdocument,
24
Schema,
35
Document,
46
HydratedDocument,
@@ -1442,3 +1444,53 @@ function gh14367() {
14421444
flags: [true]
14431445
};
14441446
}
1447+
1448+
function gh14573() {
1449+
interface Names {
1450+
_id: Types.ObjectId;
1451+
firstName: string;
1452+
}
1453+
1454+
// Document definition
1455+
interface User {
1456+
names: Names;
1457+
}
1458+
1459+
// Define property overrides for hydrated documents
1460+
type THydratedUserDocument = {
1461+
names?: HydratedSingleSubdocument<Names>;
1462+
};
1463+
1464+
type UserMethods = {
1465+
getName(): Names | undefined;
1466+
};
1467+
1468+
type UserModelType = Model<User, {}, UserMethods, {}, THydratedUserDocument>;
1469+
1470+
const userSchema = new Schema<
1471+
User,
1472+
UserModelType,
1473+
UserMethods,
1474+
{},
1475+
{},
1476+
{},
1477+
DefaultSchemaOptions,
1478+
User,
1479+
THydratedUserDocument
1480+
>(
1481+
{
1482+
names: new Schema<Names>({ firstName: String })
1483+
},
1484+
{
1485+
methods: {
1486+
getName() {
1487+
const str: string | undefined = this.names?.firstName;
1488+
return this.names?.toObject();
1489+
}
1490+
}
1491+
}
1492+
);
1493+
const UserModel = model<User, UserModelType>('User', userSchema);
1494+
const doc = new UserModel({ names: { _id: '0'.repeat(24), firstName: 'foo' } });
1495+
doc.names?.ownerDocument();
1496+
}

0 commit comments

Comments
 (0)