Skip to content

Commit

Permalink
Merge pull request #14575 from Automattic/vkarpov15/gh-14573
Browse files Browse the repository at this point in the history
docs(typescript): clarify that setting THydratedDocumentType on schemas is necessary for correct method context
  • Loading branch information
vkarpov15 authored May 9, 2024
2 parents 88afc33 + e2b0e12 commit d5202fb
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 4 deletions.
23 changes: 19 additions & 4 deletions docs/typescript/schemas.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ This is because Mongoose has numerous features that add paths to your schema tha
## Arrays

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.
Instead, use the `THydratedDocumentType` generic to define that the hydrated document type has paths of type `Types.Array` and `Types.DocumentArray`.
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`.

```typescript
import mongoose from 'mongoose'
Expand All @@ -178,17 +178,27 @@ interface IOrder {
// for fully hydrated docs returned from `findOne()`, etc.
type OrderHydratedDocument = mongoose.HydratedDocument<
IOrder,
{ tags: mongoose.Types.DocumentArray<{ name: string }> }
{ tags: mongoose.HydratedArraySubdocument<{ name: string }> }
>;
type OrderModelType = mongoose.Model<
IOrder,
{},
{},
{},
OrderHydratedDocument
OrderHydratedDocument // THydratedDocumentType
>;

const orderSchema = new mongoose.Schema<IOrder, OrderModelType>({
const orderSchema = new mongoose.Schema<
IOrder,
OrderModelType,
{}, // methods
{}, // query helpers
{}, // virtuals
{}, // statics
mongoose.DefaultSchemaOptions, // schema options
IOrder, // doctype
OrderHydratedDocument // THydratedDocumentType
>({
tags: [{ name: { type: String, required: true } }]
});
const OrderModel = mongoose.model<IOrder, OrderModelType>('Order', orderSchema);
Expand All @@ -207,3 +217,8 @@ async function run() {
leanDoc.tags; // Array<{ name: string }>
};
```

Use `HydratedArraySubdocument<RawDocType>` for the type of array subdocuments, and `HydratedSingleSubdocument<RawDocType>` for single subdocuments.

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>(...)`.
The THydratedDocumentType parameter for schemas is primarily for setting the value of `this` on methods and virtuals.
52 changes: 52 additions & 0 deletions test/types/schema.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import {
DefaultSchemaOptions,
HydratedSingleSubdocument,
Schema,
Document,
HydratedDocument,
Expand Down Expand Up @@ -1442,3 +1444,53 @@ function gh14367() {
flags: [true]
};
}

function gh14573() {
interface Names {
_id: Types.ObjectId;
firstName: string;
}

// Document definition
interface User {
names: Names;
}

// Define property overrides for hydrated documents
type THydratedUserDocument = {
names?: HydratedSingleSubdocument<Names>;
};

type UserMethods = {
getName(): Names | undefined;
};

type UserModelType = Model<User, {}, UserMethods, {}, THydratedUserDocument>;

const userSchema = new Schema<
User,
UserModelType,
UserMethods,
{},
{},
{},
DefaultSchemaOptions,
User,
THydratedUserDocument
>(
{
names: new Schema<Names>({ firstName: String })
},
{
methods: {
getName() {
const str: string | undefined = this.names?.firstName;
return this.names?.toObject();
}
}
}
);
const UserModel = model<User, UserModelType>('User', userSchema);
const doc = new UserModel({ names: { _id: '0'.repeat(24), firstName: 'foo' } });
doc.names?.ownerDocument();
}

0 comments on commit d5202fb

Please sign in to comment.