Skip to content

types(document): respect schema toObject and toJSON options - #16431

Merged
vkarpov15 merged 1 commit into
Automattic:masterfrom
samuelmbabhazi:fix/schemaToObjectOptions
Aug 5, 2026
Merged

types(document): respect schema toObject and toJSON options#16431
vkarpov15 merged 1 commit into
Automattic:masterfrom
samuelmbabhazi:fix/schemaToObjectOptions

Conversation

@samuelmbabhazi

Copy link
Copy Markdown
Contributor

Summary

Fixes #15594

At runtime, toObject() and toJSON() apply the options declared in the schema definition as defaults: $toObject merges _defaultToObjectOptions under the call options. The declared return types did not reflect that. Options passed at the call site have influenced the return type since #15578, but the no argument overloads always returned the plain document shape, so flattenObjectIds, versionKey: false or virtuals: true declared at the schema level were invisible to TypeScript.

This change routes the no argument overloads, including the populated document variants, through the existing ToObjectReturnType machinery. A new SchemaDeclaredToObjectOptions helper extracts the toObject/toJSON options captured in TSchemaOptions, and DefaultToObjectReturnType short circuits to the exact previous return type when the schema declares no options for the method, so schemas without declared options see no type change at all.

Examples

const schema = new Schema({
  testProperty: Number,
  testId: Schema.Types.ObjectId
}, {
  toObject: { flattenObjectIds: true, versionKey: false },
  toJSON: { flattenObjectIds: true, versionKey: false }
});
const TestModel = model('Test', schema);
const doc = new TestModel({ testProperty: 8, testId: new Types.ObjectId() });

doc.toObject()._id; // before: Types.ObjectId. After: string
doc.toObject(); // before: __v present in the type. After: no __v, matching the runtime output
doc.toObject({ flattenObjectIds: false })._id; // still Types.ObjectId, call options keep priority

One note on the snippet in #15594: it constructs the schema as new Schema<ASchema>(definition, options). TypeScript has no partial inference, so an explicit raw doc type generic prevents TSchemaOptions from capturing the options literal, for this feature the same way as for timestamps typing. The schema level options propagate on the inference paths: new Schema(definition, options) without explicit generics, Schema.create, or an explicitly supplied TSchemaOptions generic.

New type tests (gh15594) cover a schema with declared options, the unchanged shape for schemas without declared options, call site precedence, a schema declaring only toJSON options, and a custom version key combined with toObject: { versionKey: false }. npm run test:types passes with 40 files and 1049 assertions. npm run lint-ts is clean.

@vkarpov15 vkarpov15 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks 👍

@vkarpov15 vkarpov15 added this to the 9.9.2 milestone Aug 5, 2026
@vkarpov15
vkarpov15 merged commit 5f1188f into Automattic:master Aug 5, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

toObject() and toJSON() schemas options should propagate

2 participants