Skip to content

Commit

Permalink
Merge pull request #14525 from Automattic/vkarpov15/gh-14441
Browse files Browse the repository at this point in the history
types(query+populate): apply populate overrides to doc `toObject()` result
  • Loading branch information
vkarpov15 committed Apr 24, 2024
2 parents e91fcf4 + 5b2545e commit 5137eeb
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 4 deletions.
46 changes: 46 additions & 0 deletions test/types/populate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -373,3 +373,49 @@ async function gh13070() {
const doc2 = await Child.populate<{ child: IChild }>(doc, 'child');
const name: string = doc2.child.name;
}

function gh14441() {
interface Parent {
child?: Types.ObjectId;
name?: string;
}
const ParentModel = model<Parent>(
'Parent',
new Schema({
child: { type: Schema.Types.ObjectId, ref: 'Child' },
name: String
})
);

interface Child {
name: string;
}
const childSchema: Schema = new Schema({ name: String });
model<Child>('Child', childSchema);

ParentModel.findOne({})
.populate<{ child: Child }>('child')
.orFail()
.then(doc => {
expectType<string>(doc.child.name);
const docObject = doc.toObject();
expectType<string>(docObject.child.name);
});

ParentModel.findOne({})
.populate<{ child: Child }>('child')
.lean()
.orFail()
.then(doc => {
expectType<string>(doc.child.name);
});

ParentModel.find({})
.populate<{ child: Child }>('child')
.orFail()
.then(docs => {
expectType<string>(docs[0]!.child.name);
const docObject = docs[0]!.toObject();
expectType<string>(docObject.child.name);
});
}
24 changes: 24 additions & 0 deletions test/types/queries.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -612,3 +612,27 @@ function gh14473() {
const query2: FilterQuery<D> = { deletedAt: { $lt: new Date() } };
};
}

async function gh14525() {
type BeAnObject = Record<string, any>;

interface SomeDoc {
something: string;
func(this: TestDoc): string;
}

interface PluginExtras {
pfunc(): number;
}

type TestDoc = Document<unknown, BeAnObject, SomeDoc> & PluginExtras;

type ModelType = Model<SomeDoc, BeAnObject, PluginExtras, BeAnObject>;

const doc = await ({} as ModelType).findOne({}).populate('test').orFail().exec();

doc.func();

let doc2 = await ({} as ModelType).create({});
doc2 = await ({} as ModelType).findOne({}).populate('test').orFail().exec();
}
41 changes: 37 additions & 4 deletions types/query.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,18 @@ declare module 'mongoose' {
? (ResultType extends any[] ? Require_id<FlattenMaps<RawDocType>>[] : Require_id<FlattenMaps<RawDocType>>)
: ResultType;

type MergePopulatePaths<RawDocType, ResultType, QueryOp, Paths, TQueryHelpers> = QueryOp extends QueryOpThatReturnsDocument
? ResultType extends null
? ResultType
: ResultType extends (infer U)[]
? U extends Document
? HydratedDocument<MergeType<RawDocType, Paths>, Record<string, never>, TQueryHelpers>[]
: (MergeType<U, Paths>)[]
: ResultType extends Document
? HydratedDocument<MergeType<RawDocType, Paths>, Record<string, never>, TQueryHelpers>
: MergeType<ResultType, Paths>
: MergeType<ResultType, Paths>;

class Query<ResultType, DocType, THelpers = {}, RawDocType = DocType, QueryOp = 'find'> implements SessionOperation {
_mongooseOptions: MongooseQueryOptions<DocType>;

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

/** Specifies paths which should be populated with other documents. */
populate<Paths = {}>(
populate(
path: string | string[],
select?: string | any,
model?: string | Model<any, THelpers>,
match?: any
): QueryWithHelpers<
ResultType,
DocType,
THelpers,
RawDocType,
QueryOp
>;
populate(
options: PopulateOptions | (PopulateOptions | string)[]
): QueryWithHelpers<
ResultType,
DocType,
THelpers,
RawDocType,
QueryOp
>;
populate<Paths>(
path: string | string[],
select?: string | any,
model?: string | Model<any, THelpers>,
match?: any
): QueryWithHelpers<
UnpackedIntersection<ResultType, Paths>,
MergePopulatePaths<RawDocType, ResultType, QueryOp, Paths, THelpers>,
DocType,
THelpers,
UnpackedIntersection<RawDocType, Paths>,
QueryOp
>;
populate<Paths = {}>(
populate<Paths>(
options: PopulateOptions | (PopulateOptions | string)[]
): QueryWithHelpers<
UnpackedIntersection<ResultType, Paths>,
MergePopulatePaths<RawDocType, ResultType, QueryOp, Paths, THelpers>,
DocType,
THelpers,
UnpackedIntersection<RawDocType, Paths>,
Expand Down

0 comments on commit 5137eeb

Please sign in to comment.