Skip to content

Commit

Permalink
Merge branch 'master' into 8.4
Browse files Browse the repository at this point in the history
  • Loading branch information
vkarpov15 committed May 10, 2024
2 parents 822392e + 11c754c commit b9d726d
Show file tree
Hide file tree
Showing 5 changed files with 244 additions and 99 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.
41 changes: 41 additions & 0 deletions test/types/populate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -419,3 +419,44 @@ function gh14441() {
expectType<string>(docObject.child.name);
});
}

async function gh14574() {
// Document definition
interface User {
firstName: string;
lastName: string;
friend?: Types.ObjectId;
}

interface UserMethods {
fullName(): string;
}

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

const userSchema = new Schema<User, UserModelType, UserMethods>(
{
firstName: String,
lastName: String,
friend: { type: Schema.Types.ObjectId, ref: 'User' }
},
{
methods: {
fullName() {
return `${this.firstName} ${this.lastName}`;
}
}
}
);
const userModel = model<User, UserModelType>('User', userSchema);

const UserModel = () => userModel;

const user = await UserModel()
.findOne({ firstName: 'b' })
.populate<{ friend: HydratedDocument<User, UserMethods> }>('friend')
.orFail()
.exec();
expectType<string>(user.fullName());
expectType<string>(user.friend.fullName());
}
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 b9d726d

Please sign in to comment.