Skip to content

Commit 5137eeb

Browse files
authored
Merge pull request #14525 from Automattic/vkarpov15/gh-14441
types(query+populate): apply populate overrides to doc `toObject()` result
2 parents e91fcf4 + 5b2545e commit 5137eeb

File tree

3 files changed

+107
-4
lines changed

3 files changed

+107
-4
lines changed

test/types/populate.test.ts

+46
Original file line numberDiff line numberDiff line change
@@ -373,3 +373,49 @@ async function gh13070() {
373373
const doc2 = await Child.populate<{ child: IChild }>(doc, 'child');
374374
const name: string = doc2.child.name;
375375
}
376+
377+
function gh14441() {
378+
interface Parent {
379+
child?: Types.ObjectId;
380+
name?: string;
381+
}
382+
const ParentModel = model<Parent>(
383+
'Parent',
384+
new Schema({
385+
child: { type: Schema.Types.ObjectId, ref: 'Child' },
386+
name: String
387+
})
388+
);
389+
390+
interface Child {
391+
name: string;
392+
}
393+
const childSchema: Schema = new Schema({ name: String });
394+
model<Child>('Child', childSchema);
395+
396+
ParentModel.findOne({})
397+
.populate<{ child: Child }>('child')
398+
.orFail()
399+
.then(doc => {
400+
expectType<string>(doc.child.name);
401+
const docObject = doc.toObject();
402+
expectType<string>(docObject.child.name);
403+
});
404+
405+
ParentModel.findOne({})
406+
.populate<{ child: Child }>('child')
407+
.lean()
408+
.orFail()
409+
.then(doc => {
410+
expectType<string>(doc.child.name);
411+
});
412+
413+
ParentModel.find({})
414+
.populate<{ child: Child }>('child')
415+
.orFail()
416+
.then(docs => {
417+
expectType<string>(docs[0]!.child.name);
418+
const docObject = docs[0]!.toObject();
419+
expectType<string>(docObject.child.name);
420+
});
421+
}

test/types/queries.test.ts

+24
Original file line numberDiff line numberDiff line change
@@ -612,3 +612,27 @@ function gh14473() {
612612
const query2: FilterQuery<D> = { deletedAt: { $lt: new Date() } };
613613
};
614614
}
615+
616+
async function gh14525() {
617+
type BeAnObject = Record<string, any>;
618+
619+
interface SomeDoc {
620+
something: string;
621+
func(this: TestDoc): string;
622+
}
623+
624+
interface PluginExtras {
625+
pfunc(): number;
626+
}
627+
628+
type TestDoc = Document<unknown, BeAnObject, SomeDoc> & PluginExtras;
629+
630+
type ModelType = Model<SomeDoc, BeAnObject, PluginExtras, BeAnObject>;
631+
632+
const doc = await ({} as ModelType).findOne({}).populate('test').orFail().exec();
633+
634+
doc.func();
635+
636+
let doc2 = await ({} as ModelType).create({});
637+
doc2 = await ({} as ModelType).findOne({}).populate('test').orFail().exec();
638+
}

types/query.d.ts

+37-4
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,18 @@ declare module 'mongoose' {
205205
? (ResultType extends any[] ? Require_id<FlattenMaps<RawDocType>>[] : Require_id<FlattenMaps<RawDocType>>)
206206
: ResultType;
207207

208+
type MergePopulatePaths<RawDocType, ResultType, QueryOp, Paths, TQueryHelpers> = QueryOp extends QueryOpThatReturnsDocument
209+
? ResultType extends null
210+
? ResultType
211+
: ResultType extends (infer U)[]
212+
? U extends Document
213+
? HydratedDocument<MergeType<RawDocType, Paths>, Record<string, never>, TQueryHelpers>[]
214+
: (MergeType<U, Paths>)[]
215+
: ResultType extends Document
216+
? HydratedDocument<MergeType<RawDocType, Paths>, Record<string, never>, TQueryHelpers>
217+
: MergeType<ResultType, Paths>
218+
: MergeType<ResultType, Paths>;
219+
208220
class Query<ResultType, DocType, THelpers = {}, RawDocType = DocType, QueryOp = 'find'> implements SessionOperation {
209221
_mongooseOptions: MongooseQueryOptions<DocType>;
210222

@@ -602,22 +614,43 @@ declare module 'mongoose' {
602614
polygon(...coordinatePairs: number[][]): this;
603615

604616
/** Specifies paths which should be populated with other documents. */
605-
populate<Paths = {}>(
617+
populate(
618+
path: string | string[],
619+
select?: string | any,
620+
model?: string | Model<any, THelpers>,
621+
match?: any
622+
): QueryWithHelpers<
623+
ResultType,
624+
DocType,
625+
THelpers,
626+
RawDocType,
627+
QueryOp
628+
>;
629+
populate(
630+
options: PopulateOptions | (PopulateOptions | string)[]
631+
): QueryWithHelpers<
632+
ResultType,
633+
DocType,
634+
THelpers,
635+
RawDocType,
636+
QueryOp
637+
>;
638+
populate<Paths>(
606639
path: string | string[],
607640
select?: string | any,
608641
model?: string | Model<any, THelpers>,
609642
match?: any
610643
): QueryWithHelpers<
611-
UnpackedIntersection<ResultType, Paths>,
644+
MergePopulatePaths<RawDocType, ResultType, QueryOp, Paths, THelpers>,
612645
DocType,
613646
THelpers,
614647
UnpackedIntersection<RawDocType, Paths>,
615648
QueryOp
616649
>;
617-
populate<Paths = {}>(
650+
populate<Paths>(
618651
options: PopulateOptions | (PopulateOptions | string)[]
619652
): QueryWithHelpers<
620-
UnpackedIntersection<ResultType, Paths>,
653+
MergePopulatePaths<RawDocType, ResultType, QueryOp, Paths, THelpers>,
621654
DocType,
622655
THelpers,
623656
UnpackedIntersection<RawDocType, Paths>,

0 commit comments

Comments
 (0)