types(populate): retain populated paths in toObject() and toJSON() unless depopulate: true set#16089
types(populate): retain populated paths in toObject() and toJSON() unless depopulate: true set#16089
Conversation
…less depopulate: true set Fix #16085
There was a problem hiding this comment.
Pull request overview
This PR updates Mongoose’s TypeScript populate typings so that populated path types are preserved through toObject() / toJSON() (for document, query, and model populate()), while still returning depopulated/raw path types when calling toObject({ depopulate: true }) / toJSON({ depopulate: true }).
Changes:
- Introduces a
PopulateDocumentResulthelper type that overridestoObject()/toJSON()return types based on whetherdepopulate: trueis set. - Updates
Document.populate(),Document.$assertPopulated(),Model.populate(), and query populate result typing to returnPopulateDocumentResult. - Adds type tests covering populated arrays and depopulate behavior for
toObject().
Reviewed changes
Copilot reviewed 1 out of 5 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
types/query.d.ts |
Wraps populated query document results in PopulateDocumentResult to preserve populated serialization typings. |
types/populate.d.ts |
Adds PopulateDocumentResult (+ related helpers) to override toObject() / toJSON() return types based on depopulate. |
types/models.d.ts |
Updates Model.populate<Paths>() return types to use PopulateDocumentResult. |
types/document.d.ts |
Updates Document.populate() and $assertPopulated() return types to use PopulateDocumentResult. |
test/types/populate.test.ts |
Adds compile-time assertions for populated array toObject() and depopulated toObject({ depopulate: true }). |
| ExpectType<string | undefined>(populatedDoc.children[0]?.name); | ||
| const plainObject = populatedDoc.toObject(); | ||
| ExpectType<string>(plainObject.children[0].name); | ||
| const depopulatedObject = populatedDoc.toObject({ depopulate: true }); | ||
| ExpectType<Types.ObjectId>(depopulatedObject.children![0]); | ||
| }); | ||
|
|
||
| ArrayParentModel.findOne({}) | ||
| .populate<PopulatedChildren>('children') | ||
| .orFail() | ||
| .then(populatedDoc => { | ||
| ExpectAssignable<Document<any>>()(populatedDoc); | ||
| ExpectAssignable<Document<unknown>>()(populatedDoc); | ||
| ExpectType<string | undefined>(populatedDoc.children[0]?.name); | ||
| const plainObject = populatedDoc.toObject(); | ||
| ExpectType<string>(plainObject.children[0].name); | ||
| const depopulatedObject = populatedDoc.toObject({ depopulate: true }); | ||
| ExpectType<Types.ObjectId>(depopulatedObject.children![0]); |
There was a problem hiding this comment.
This hunk adds type assertions for toObject() and toObject({ depopulate: true }) on populated docs, but the PR also changes toJSON() typings. To prevent regressions, add equivalent assertions for toJSON() and toJSON({ depopulate: true }) on these populated results (doc.populate, query.populate, and Model.populate cases).
Fix #16085
Summary
The populate typing fix now preserves populated path types through
toObject()/toJSON()for document, query, and modelpopulate()calls, while still returning the original raw path types fortoObject({ depopulate: true }).The primary change is a new
PopulateDocumentResulttype that returns a document with overwrittentoObject()andtoJSON()that accounts for populated pathsExamples