From 52c75735a20853d1e65cc2f01cbf70e55b76b923 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Sat, 5 Jul 2025 14:36:29 -0400 Subject: [PATCH 01/18] types: avoid FlattenMaps by default on toObject(), toJSON(), lean() Re: #13523 --- test/types/lean.test.ts | 18 ++++++ test/types/maps.test.ts | 2 +- test/types/models.test.ts | 7 ++- test/types/schema.create.test.ts | 31 +++++++--- types/document.d.ts | 35 ++++++++---- types/index.d.ts | 62 ++++++++++++-------- types/inferrawdoctype.d.ts | 4 +- types/inferschematype.d.ts | 5 +- types/models.d.ts | 97 ++++++++++++++++---------------- types/query.d.ts | 2 +- 10 files changed, 165 insertions(+), 98 deletions(-) diff --git a/test/types/lean.test.ts b/test/types/lean.test.ts index a35d0f7ad00..eb8196412a1 100644 --- a/test/types/lean.test.ts +++ b/test/types/lean.test.ts @@ -144,6 +144,24 @@ async function gh13010() { expectType>(country.name); } +async function gh13010_1() { + const schema = Schema.create({ + name: { required: true, type: Map, of: String } + }); + + const CountryModel = model('Country', schema); + + await CountryModel.create({ + name: { + en: 'Croatia', + ru: 'Хорватия' + } + }); + + const country = await CountryModel.findOne().lean().orFail().exec(); + expectType>(country.name); +} + async function gh13345_1() { const imageSchema = new Schema({ url: { required: true, type: String } diff --git a/test/types/maps.test.ts b/test/types/maps.test.ts index 69cd016c74f..78173734569 100644 --- a/test/types/maps.test.ts +++ b/test/types/maps.test.ts @@ -70,7 +70,7 @@ function gh10575() { function gh10872(): void { const doc = new Test({}); - doc.toJSON().map1.foo; + doc.toJSON({ flattenMaps: true }).map1.foo; } function gh13755() { diff --git a/test/types/models.test.ts b/test/types/models.test.ts index d7474e61ef6..f6d4f739082 100644 --- a/test/types/models.test.ts +++ b/test/types/models.test.ts @@ -16,7 +16,8 @@ import mongoose, { WithLevel1NestedPaths, createConnection, connection, - model + model, + ObtainSchemaGeneric } from 'mongoose'; import { expectAssignable, expectError, expectType } from 'tsd'; import { AutoTypedSchemaType, autoTypedSchema } from './schema.test'; @@ -575,12 +576,14 @@ async function gh12319() { ); const ProjectModel = model('Project', projectSchema); + const doc = new ProjectModel(); + doc.doSomething(); type ProjectModelHydratedDoc = HydratedDocumentFromSchema< typeof projectSchema >; - expectType(await ProjectModel.findOne().orFail()); + expectAssignable(await ProjectModel.findOne().orFail()); } function findWithId() { diff --git a/test/types/schema.create.test.ts b/test/types/schema.create.test.ts index 3c827db6ca1..f8d3cc9c721 100644 --- a/test/types/schema.create.test.ts +++ b/test/types/schema.create.test.ts @@ -413,8 +413,8 @@ export function autoTypedSchema() { objectId2?: Types.ObjectId | null; objectId3?: Types.ObjectId | null; customSchema?: Int8 | null; - map1?: Map | null; - map2?: Map | null; + map1?: Record | null; + map2?: Record | null; array1: string[]; array2: any[]; array3: any[]; @@ -734,17 +734,26 @@ function gh12030() { } & { _id: Types.ObjectId }>; } & { _id: Types.ObjectId }>({} as InferSchemaType); + type RawDocType3 = ObtainSchemaGeneric; type HydratedDoc3 = ObtainSchemaGeneric; expectType< HydratedDocument<{ users: Types.DocumentArray< { credit: number; username?: string | null; } & { _id: Types.ObjectId }, - Types.Subdocument & { credit: number; username?: string | null; } & { _id: Types.ObjectId } + Types.Subdocument< + Types.ObjectId, + unknown, + { credit: number; username?: string | null; } & { _id: Types.ObjectId } + > & { credit: number; username?: string | null; } & { _id: Types.ObjectId } >; - } & { _id: Types.ObjectId }> + } & { _id: Types.ObjectId }, {}, {}, {}, RawDocType3> >({} as HydratedDoc3); expectType< - Types.Subdocument & { credit: number; username?: string | null; } & { _id: Types.ObjectId } + Types.Subdocument< + Types.ObjectId, + unknown, + { credit: number; username?: string | null; } & { _id: Types.ObjectId } + > & { credit: number; username?: string | null; } & { _id: Types.ObjectId } >({} as HydratedDoc3['users'][0]); const Schema4 = Schema.create({ @@ -1164,6 +1173,9 @@ function maps() { const doc = new Test({ myMap: { answer: 42 } }); expectType>(doc.myMap); expectType(doc.myMap!.get('answer')); + + const obj = doc.toObject(); + expectType>(obj.myMap); } function gh13514() { @@ -1697,7 +1709,12 @@ async function gh14950() { const doc = await TestModel.findOne().orFail(); expectType(doc.location!.type); - expectType(doc.location!.coordinates); + expectType>(doc.location!.coordinates); + + const lean = await TestModel.findOne().lean().orFail(); + + expectType(lean.location!.type); + expectType(lean.location!.coordinates); } async function gh14902() { @@ -1748,7 +1765,7 @@ async function gh14451() { subdocProp?: string | undefined | null } | null, docArr: { nums: number[], times: string[] }[], - myMap?: Record | null | undefined, + myMap?: Record | null | undefined, _id: string }>({} as TestJSON); } diff --git a/types/document.d.ts b/types/document.d.ts index dd19f755ac6..a733e2b5ef7 100644 --- a/types/document.d.ts +++ b/types/document.d.ts @@ -256,23 +256,34 @@ declare module 'mongoose' { set(value: string | Record): this; /** The return value of this method is used in calls to JSON.stringify(doc). */ + toJSON(options: ToObjectOptions & { flattenMaps: true, flattenObjectIds: true, virtuals: true }): FlattenMaps>>>; + toJSON(options: ToObjectOptions & { flattenMaps: true, flattenObjectIds: true }): FlattenMaps>>>; + toJSON(options: ToObjectOptions & { flattenMaps: true, virtuals: true }): FlattenMaps>>; + toJSON(options: ToObjectOptions & { flattenObjectIds: true, virtuals: true }): ObjectIdToString>>; + toJSON(options: ToObjectOptions & { flattenMaps: true }): FlattenMaps>>; + toJSON(options: ToObjectOptions & { flattenObjectIds: true }): ObjectIdToString>>; toJSON(options: ToObjectOptions & { virtuals: true }): Default__v>; - toJSON(options?: ToObjectOptions & { flattenMaps?: true, flattenObjectIds?: false }): FlattenMaps>>; - toJSON(options: ToObjectOptions & { flattenObjectIds: false }): FlattenMaps>>; - toJSON(options: ToObjectOptions & { flattenObjectIds: true }): ObjectIdToString>>>; - toJSON(options: ToObjectOptions & { flattenMaps: false }): Default__v>; - toJSON(options: ToObjectOptions & { flattenMaps: false; flattenObjectIds: true }): ObjectIdToString>>; - - toJSON>>(options?: ToObjectOptions & { flattenMaps?: true, flattenObjectIds?: false }): FlattenMaps; - toJSON>>(options: ToObjectOptions & { flattenObjectIds: false }): FlattenMaps; - toJSON>>(options: ToObjectOptions & { flattenObjectIds: true }): ObjectIdToString>; - toJSON>>(options: ToObjectOptions & { flattenMaps: false }): T; - toJSON>>(options: ToObjectOptions & { flattenMaps: false; flattenObjectIds: true }): ObjectIdToString; + toJSON(options?: ToObjectOptions): Default__v>; + + toJSON>>(options: ToObjectOptions & { flattenMaps: true, flattenObjectIds: true }): FlattenMaps>; + toJSON>>(options: ToObjectOptions & { flattenObjectIds: true }): ObjectIdToString; + toJSON>>(options: ToObjectOptions & { flattenMaps: true }): FlattenMaps; + toJSON>>(options?: ToObjectOptions): T; /** Converts this document into a plain-old JavaScript object ([POJO](https://masteringjs.io/tutorials/fundamentals/pojo)). */ + toObject(options: ToObjectOptions & { flattenMaps: true, flattenObjectIds: true, virtuals: true }): FlattenMaps>>>; + toObject(options: ToObjectOptions & { flattenMaps: true, flattenObjectIds: true }): FlattenMaps>>>; + toObject(options: ToObjectOptions & { flattenMaps: true, virtuals: true }): FlattenMaps>>; + toObject(options: ToObjectOptions & { flattenObjectIds: true, virtuals: true }): ObjectIdToString>>; + toObject(options: ToObjectOptions & { flattenMaps: true }): FlattenMaps>>; + toObject(options: ToObjectOptions & { flattenObjectIds: true }): ObjectIdToString>>; toObject(options: ToObjectOptions & { virtuals: true }): Default__v>; toObject(options?: ToObjectOptions): Default__v>; - toObject(options?: ToObjectOptions): Default__v>; + + toObject>>(options: ToObjectOptions & { flattenMaps: true, flattenObjectIds: true }): FlattenMaps>; + toObject>>(options: ToObjectOptions & { flattenObjectIds: true }): ObjectIdToString; + toObject>>(options: ToObjectOptions & { flattenMaps: true }): FlattenMaps; + toObject>>(options?: ToObjectOptions): T; /** Clears the modified state on the specified path. */ unmarkModified(path: T): void; diff --git a/types/index.d.ts b/types/index.d.ts index 942071a7591..126eb6a308b 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -85,17 +85,22 @@ declare module 'mongoose' { collection?: string, options?: CompileModelOptions ): Model< - InferSchemaType, - ObtainSchemaGeneric, - ObtainSchemaGeneric, - ObtainSchemaGeneric, - HydratedDocument< - InferSchemaType, - ObtainSchemaGeneric & ObtainSchemaGeneric, - ObtainSchemaGeneric, - ObtainSchemaGeneric - >, - TSchema + InferSchemaType, + ObtainSchemaGeneric, + ObtainSchemaGeneric, + ObtainSchemaGeneric, + // If first schema generic param is set, that means we have an explicit raw doc type, + // so user should also specify a hydrated doc type if the auto inferred one isn't correct. + IsItRecordAndNotAny> extends true + ? ObtainSchemaGeneric + : HydratedDocument< + InferSchemaType, + ObtainSchemaGeneric & ObtainSchemaGeneric, + ObtainSchemaGeneric, + ObtainSchemaGeneric + >, + TSchema, + ObtainSchemaGeneric > & ObtainSchemaGeneric; export function model(name: string, schema?: Schema | Schema, collection?: string, options?: CompileModelOptions): Model; @@ -147,24 +152,26 @@ declare module 'mongoose' { /** Helper type for getting the hydrated document type from the raw document type. The hydrated document type is what `new MyModel()` returns. */ export type HydratedDocument< - DocType, + HydratedDocPathsType, TOverrides = {}, TQueryHelpers = {}, - TVirtuals = {} + TVirtuals = {}, + RawDocType = HydratedDocPathsType > = IfAny< - DocType, + HydratedDocPathsType, any, TOverrides extends Record ? - Document & Default__v> : + Document & Default__v> : IfAny< TOverrides, - Document & Default__v>, - Document & MergeType< - Default__v>, + Document & Default__v>, + Document & MergeType< + Default__v>, TOverrides > > >; + export type HydratedSingleSubdocument< DocType, TOverrides = {} @@ -274,8 +281,9 @@ declare module 'mongoose' { ObtainDocumentType>, ResolveSchemaOptions >, - THydratedDocumentType = HydratedDocument, TVirtuals & TInstanceMethods, {}, TVirtuals>, - TSchemaDefinition = SchemaDefinition, RawDocType, THydratedDocumentType> + THydratedDocumentType = HydratedDocument, + TSchemaDefinition = SchemaDefinition, RawDocType, THydratedDocumentType>, + LeanResultType = IsItRecordAndNotAny extends true ? RawDocType : Default__v>>> > extends events.EventEmitter { /** @@ -291,7 +299,13 @@ declare module 'mongoose' { InferRawDocType>, ResolveSchemaOptions >, - THydratedDocumentType extends AnyObject = HydratedDocument>> + THydratedDocumentType extends AnyObject = HydratedDocument< + InferHydratedDocType>, + TSchemaOptions extends { methods: infer M } ? M : {}, + TSchemaOptions extends { query: any } ? TSchemaOptions['query'] : {}, + TSchemaOptions extends { virtuals: any } ? TSchemaOptions['virtuals'] : {}, + RawDocType + > >(def: TSchemaDefinition): Schema< RawDocType, Model, @@ -305,7 +319,8 @@ declare module 'mongoose' { ResolveSchemaOptions >, THydratedDocumentType, - TSchemaDefinition + TSchemaDefinition, + BufferToBinary >; static create< @@ -329,7 +344,8 @@ declare module 'mongoose' { ResolveSchemaOptions >, THydratedDocumentType, - TSchemaDefinition + TSchemaDefinition, + BufferToBinary >; /** Adds key path / schema type pairs to this schema. */ diff --git a/types/inferrawdoctype.d.ts b/types/inferrawdoctype.d.ts index c40c8c48c73..2e62311d6c8 100644 --- a/types/inferrawdoctype.d.ts +++ b/types/inferrawdoctype.d.ts @@ -124,8 +124,8 @@ declare module 'mongoose' { PathValueType extends 'uuid' | 'UUID' | typeof Schema.Types.UUID ? UUID : PathValueType extends 'double' | 'Double' | typeof Schema.Types.Double ? Types.Double : IfEquals extends true ? Buffer : - PathValueType extends MapConstructor | 'Map' ? Map> : - IfEquals extends true ? Map> : + PathValueType extends MapConstructor | 'Map' ? Record | undefined> : + IfEquals extends true ? Record | undefined> : PathValueType extends ArrayConstructor ? any[] : PathValueType extends typeof Schema.Types.Mixed ? any: IfEquals extends true ? any: diff --git a/types/inferschematype.d.ts b/types/inferschematype.d.ts index 2b01fd40c9e..8149e60d370 100644 --- a/types/inferschematype.d.ts +++ b/types/inferschematype.d.ts @@ -56,8 +56,8 @@ declare module 'mongoose' { * @param {TSchema} TSchema A generic of schema type instance. * @param {alias} alias Targeted generic alias. */ - type ObtainSchemaGeneric = - TSchema extends Schema + type ObtainSchemaGeneric = + TSchema extends Schema ? { EnforcedDocType: EnforcedDocType; M: M; @@ -69,6 +69,7 @@ declare module 'mongoose' { DocType: DocType; THydratedDocumentType: THydratedDocumentType; TSchemaDefinition: TSchemaDefinition; + TLeanResultType: TLeanResultType; }[alias] : unknown; diff --git a/types/models.d.ts b/types/models.d.ts index bc205aed74c..df9ac8e6ae0 100644 --- a/types/models.d.ts +++ b/types/models.d.ts @@ -264,7 +264,8 @@ declare module 'mongoose' { TInstanceMethods = {}, TVirtuals = {}, THydratedDocumentType = HydratedDocument, - TSchema = any> extends + TSchema = any, + TLeanResultType = TRawDocType> extends NodeJS.EventEmitter, AcceptsDiscriminator, IndexManager, @@ -387,7 +388,7 @@ declare module 'mongoose' { mongodb.DeleteResult, THydratedDocumentType, TQueryHelpers, - TRawDocType, + TLeanResultType, 'deleteMany', TInstanceMethods & TVirtuals >; @@ -404,7 +405,7 @@ declare module 'mongoose' { mongodb.DeleteResult, THydratedDocumentType, TQueryHelpers, - TRawDocType, + TLeanResultType, 'deleteOne', TInstanceMethods & TVirtuals >; @@ -414,7 +415,7 @@ declare module 'mongoose' { mongodb.DeleteResult, THydratedDocumentType, TQueryHelpers, - TRawDocType, + TLeanResultType, 'deleteOne', TInstanceMethods & TVirtuals >; @@ -444,7 +445,7 @@ declare module 'mongoose' { GetLeanResultType | null, ResultDoc, TQueryHelpers, - TRawDocType, + TLeanResultType, 'findOne', TInstanceMethods & TVirtuals >; @@ -467,7 +468,7 @@ declare module 'mongoose' { GetLeanResultType | null, ResultDoc, TQueryHelpers, - TRawDocType, + TLeanResultType, 'findOne', TInstanceMethods & TVirtuals >; @@ -475,14 +476,14 @@ declare module 'mongoose' { filter?: RootFilterQuery, projection?: ProjectionType | null, options?: QueryOptions | null - ): QueryWithHelpers; + ): QueryWithHelpers; findOne( filter?: RootFilterQuery, projection?: ProjectionType | null - ): QueryWithHelpers; + ): QueryWithHelpers; findOne( filter?: RootFilterQuery - ): QueryWithHelpers; + ): QueryWithHelpers; /** * Shortcut for creating a new Document from existing raw data, pre-saved in the DB. @@ -659,7 +660,7 @@ declare module 'mongoose' { >, THydratedDocumentType, TQueryHelpers, - TRawDocType, + TLeanResultType, 'distinct', TInstanceMethods & TVirtuals >; @@ -669,7 +670,7 @@ declare module 'mongoose' { number, THydratedDocumentType, TQueryHelpers, - TRawDocType, + TLeanResultType, 'estimatedDocumentCount', TInstanceMethods & TVirtuals >; @@ -684,7 +685,7 @@ declare module 'mongoose' { { _id: InferId } | null, THydratedDocumentType, TQueryHelpers, - TRawDocType, + TLeanResultType, 'findOne', TInstanceMethods & TVirtuals >; @@ -698,7 +699,7 @@ declare module 'mongoose' { GetLeanResultType, ResultDoc, TQueryHelpers, - TRawDocType, + TLeanResultType, 'find', TInstanceMethods & TVirtuals >; @@ -706,37 +707,37 @@ declare module 'mongoose' { filter: RootFilterQuery, projection?: ProjectionType | null | undefined, options?: QueryOptions | null | undefined - ): QueryWithHelpers, ResultDoc, TQueryHelpers, TRawDocType, 'find', TInstanceMethods & TVirtuals>; + ): QueryWithHelpers, ResultDoc, TQueryHelpers, TLeanResultType, 'find', TInstanceMethods & TVirtuals>; find( filter: RootFilterQuery, projection?: ProjectionType | null | undefined - ): QueryWithHelpers, ResultDoc, TQueryHelpers, TRawDocType, 'find', TInstanceMethods & TVirtuals>; + ): QueryWithHelpers, ResultDoc, TQueryHelpers, TLeanResultType, 'find', TInstanceMethods & TVirtuals>; find( filter: RootFilterQuery - ): QueryWithHelpers, ResultDoc, TQueryHelpers, TRawDocType, 'find', TInstanceMethods & TVirtuals>; + ): QueryWithHelpers, ResultDoc, TQueryHelpers, TLeanResultType, 'find', TInstanceMethods & TVirtuals>; find( - ): QueryWithHelpers, ResultDoc, TQueryHelpers, TRawDocType, 'find', TInstanceMethods & TVirtuals>; + ): QueryWithHelpers, ResultDoc, TQueryHelpers, TLeanResultType, 'find', TInstanceMethods & TVirtuals>; /** Creates a `findByIdAndDelete` query, filtering by the given `_id`. */ findByIdAndDelete( id: mongodb.ObjectId | any, options: QueryOptions & { lean: true } ): QueryWithHelpers< - GetLeanResultType | null, + TLeanResultType | null, ResultDoc, TQueryHelpers, - TRawDocType, + TLeanResultType, 'findOneAndDelete', TInstanceMethods & TVirtuals >; findByIdAndDelete( id: mongodb.ObjectId | any, options: QueryOptions & { includeResultMetadata: true } - ): QueryWithHelpers, ResultDoc, TQueryHelpers, TRawDocType, 'findOneAndDelete', TInstanceMethods & TVirtuals>; + ): QueryWithHelpers, ResultDoc, TQueryHelpers, TLeanResultType, 'findOneAndDelete', TInstanceMethods & TVirtuals>; findByIdAndDelete( id?: mongodb.ObjectId | any, options?: QueryOptions | null - ): QueryWithHelpers; + ): QueryWithHelpers; /** Creates a `findOneAndUpdate` query, filtering by the given `_id`. */ findByIdAndUpdate( @@ -747,7 +748,7 @@ declare module 'mongoose' { ModifyResult, ResultDoc, TQueryHelpers, - TRawDocType, + TLeanResultType, 'findOneAndUpdate', TInstanceMethods & TVirtuals >; @@ -756,10 +757,10 @@ declare module 'mongoose' { update: UpdateQuery, options: QueryOptions & { lean: true } ): QueryWithHelpers< - GetLeanResultType | null, + TLeanResultType | null, ResultDoc, TQueryHelpers, - TRawDocType, + TLeanResultType, 'findOneAndUpdate', TInstanceMethods & TVirtuals >; @@ -767,42 +768,42 @@ declare module 'mongoose' { id: mongodb.ObjectId | any, update: UpdateQuery, options: QueryOptions & { includeResultMetadata: true } - ): QueryWithHelpers, ResultDoc, TQueryHelpers, TRawDocType, 'findOneAndUpdate', TInstanceMethods & TVirtuals>; + ): QueryWithHelpers, ResultDoc, TQueryHelpers, TLeanResultType, 'findOneAndUpdate', TInstanceMethods & TVirtuals>; findByIdAndUpdate( id: mongodb.ObjectId | any, update: UpdateQuery, options: QueryOptions & { upsert: true } & ReturnsNewDoc - ): QueryWithHelpers; + ): QueryWithHelpers; findByIdAndUpdate( id?: mongodb.ObjectId | any, update?: UpdateQuery, options?: QueryOptions | null - ): QueryWithHelpers; + ): QueryWithHelpers; findByIdAndUpdate( id: mongodb.ObjectId | any, update: UpdateQuery - ): QueryWithHelpers; + ): QueryWithHelpers; /** Creates a `findOneAndDelete` query: atomically finds the given document, deletes it, and returns the document as it was before deletion. */ findOneAndDelete( filter: RootFilterQuery, options: QueryOptions & { lean: true } ): QueryWithHelpers< - GetLeanResultType | null, + TLeanResultType | null, ResultDoc, TQueryHelpers, - TRawDocType, + TLeanResultType, 'findOneAndDelete', TInstanceMethods & TVirtuals >; findOneAndDelete( filter: RootFilterQuery, options: QueryOptions & { includeResultMetadata: true } - ): QueryWithHelpers, ResultDoc, TQueryHelpers, TRawDocType, 'findOneAndDelete', TInstanceMethods & TVirtuals>; + ): QueryWithHelpers, ResultDoc, TQueryHelpers, TLeanResultType, 'findOneAndDelete', TInstanceMethods & TVirtuals>; findOneAndDelete( filter?: RootFilterQuery | null, options?: QueryOptions | null - ): QueryWithHelpers; + ): QueryWithHelpers; /** Creates a `findOneAndReplace` query: atomically finds the given document and replaces it with `replacement`. */ findOneAndReplace( @@ -810,10 +811,10 @@ declare module 'mongoose' { replacement: TRawDocType | AnyObject, options: QueryOptions & { lean: true } ): QueryWithHelpers< - GetLeanResultType | null, + TLeanResultType | null, ResultDoc, TQueryHelpers, - TRawDocType, + TLeanResultType, 'findOneAndReplace', TInstanceMethods & TVirtuals >; @@ -821,17 +822,17 @@ declare module 'mongoose' { filter: RootFilterQuery, replacement: TRawDocType | AnyObject, options: QueryOptions & { includeResultMetadata: true } - ): QueryWithHelpers, ResultDoc, TQueryHelpers, TRawDocType, 'findOneAndReplace', TInstanceMethods & TVirtuals>; + ): QueryWithHelpers, ResultDoc, TQueryHelpers, TLeanResultType, 'findOneAndReplace', TInstanceMethods & TVirtuals>; findOneAndReplace( filter: RootFilterQuery, replacement: TRawDocType | AnyObject, options: QueryOptions & { upsert: true } & ReturnsNewDoc - ): QueryWithHelpers; + ): QueryWithHelpers; findOneAndReplace( filter?: RootFilterQuery, replacement?: TRawDocType | AnyObject, options?: QueryOptions | null - ): QueryWithHelpers; + ): QueryWithHelpers; /** Creates a `findOneAndUpdate` query: atomically find the first document that matches `filter` and apply `update`. */ findOneAndUpdate( @@ -842,7 +843,7 @@ declare module 'mongoose' { ModifyResult, ResultDoc, TQueryHelpers, - TRawDocType, + TLeanResultType, 'findOneAndUpdate', TInstanceMethods & TVirtuals >; @@ -854,7 +855,7 @@ declare module 'mongoose' { GetLeanResultType | null, ResultDoc, TQueryHelpers, - TRawDocType, + TLeanResultType, 'findOneAndUpdate', TInstanceMethods & TVirtuals >; @@ -862,24 +863,24 @@ declare module 'mongoose' { filter: RootFilterQuery, update: UpdateQuery, options: QueryOptions & { includeResultMetadata: true } - ): QueryWithHelpers, ResultDoc, TQueryHelpers, TRawDocType, 'findOneAndUpdate', TInstanceMethods & TVirtuals>; + ): QueryWithHelpers, ResultDoc, TQueryHelpers, TLeanResultType, 'findOneAndUpdate', TInstanceMethods & TVirtuals>; findOneAndUpdate( filter: RootFilterQuery, update: UpdateQuery, options: QueryOptions & { upsert: true } & ReturnsNewDoc - ): QueryWithHelpers; + ): QueryWithHelpers; findOneAndUpdate( filter?: RootFilterQuery, update?: UpdateQuery, options?: QueryOptions | null - ): QueryWithHelpers; + ): QueryWithHelpers; /** Creates a `replaceOne` query: finds the first document that matches `filter` and replaces it with `replacement`. */ replaceOne( filter?: RootFilterQuery, replacement?: TRawDocType | AnyObject, options?: (mongodb.ReplaceOptions & MongooseQueryOptions) | null - ): QueryWithHelpers; + ): QueryWithHelpers; /** Apply changes made to this model's schema after this model was compiled. */ recompileSchema(): void; @@ -892,17 +893,17 @@ declare module 'mongoose' { filter: RootFilterQuery, update: UpdateQuery | UpdateWithAggregationPipeline, options?: (mongodb.UpdateOptions & MongooseUpdateQueryOptions) | null - ): QueryWithHelpers; + ): QueryWithHelpers; /** Creates a `updateOne` query: updates the first document that matches `filter` with `update`. */ updateOne( filter: RootFilterQuery, update: UpdateQuery | UpdateWithAggregationPipeline, options?: (mongodb.UpdateOptions & MongooseUpdateQueryOptions) | null - ): QueryWithHelpers; + ): QueryWithHelpers; updateOne( update: UpdateQuery | UpdateWithAggregationPipeline - ): QueryWithHelpers; + ): QueryWithHelpers; /** Creates a Query, applies the passed conditions, and returns the Query. */ where( @@ -913,7 +914,7 @@ declare module 'mongoose' { Array, ResultDoc, TQueryHelpers, - TRawDocType, + TLeanResultType, 'find', TInstanceMethods & TVirtuals >; @@ -921,7 +922,7 @@ declare module 'mongoose' { Array, ResultDoc, TQueryHelpers, - TRawDocType, + TLeanResultType, 'find', TInstanceMethods & TVirtuals >; diff --git a/types/query.d.ts b/types/query.d.ts index 80a28924763..eee0df769be 100644 --- a/types/query.d.ts +++ b/types/query.d.ts @@ -237,7 +237,7 @@ declare module 'mongoose' { type QueryOpThatReturnsDocument = 'find' | 'findOne' | 'findOneAndUpdate' | 'findOneAndReplace' | 'findOneAndDelete'; type GetLeanResultType = QueryOp extends QueryOpThatReturnsDocument - ? (ResultType extends any[] ? Default__v>>>[] : Default__v>>>) + ? (ResultType extends any[] ? Default__v>[] : Default__v>) : ResultType; type MergePopulatePaths> = QueryOp extends QueryOpThatReturnsDocument From da378db18d869401157a6ce4bae7325239578ef4 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Sun, 6 Jul 2025 13:43:10 -0400 Subject: [PATCH 02/18] types: add TTransformOptions for InferRawDocType so RawDocType and LeanResultType can be computed by the same helper re: #13523 --- types/index.d.ts | 10 +++++-- types/inferrawdoctype.d.ts | 59 ++++++++++++++++++++------------------ 2 files changed, 39 insertions(+), 30 deletions(-) diff --git a/types/index.d.ts b/types/index.d.ts index 126eb6a308b..f69dba7b974 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -320,7 +320,10 @@ declare module 'mongoose' { >, THydratedDocumentType, TSchemaDefinition, - BufferToBinary + ApplySchemaOptions< + InferRawDocType, { bufferToBinary: true }>, + ResolveSchemaOptions + > >; static create< @@ -345,7 +348,10 @@ declare module 'mongoose' { >, THydratedDocumentType, TSchemaDefinition, - BufferToBinary + ApplySchemaOptions< + InferRawDocType, { bufferToBinary: true }>, + ResolveSchemaOptions + > >; /** Adds key path / schema type pairs to this schema. */ diff --git a/types/inferrawdoctype.d.ts b/types/inferrawdoctype.d.ts index 2e62311d6c8..a374fac84cd 100644 --- a/types/inferrawdoctype.d.ts +++ b/types/inferrawdoctype.d.ts @@ -6,19 +6,20 @@ import { PathWithTypePropertyBaseType, PathEnumOrString } from './inferschematype'; -import { UUID } from 'mongodb'; +import { Binary, UUID } from 'mongodb'; declare module 'mongoose' { export type InferRawDocType< - DocDefinition, - TSchemaOptions extends Record = DefaultSchemaOptions + SchemaDefinition, + TSchemaOptions extends Record = DefaultSchemaOptions, + TTransformOptions = { bufferToBinary: false } > = Require_id & - OptionalPaths) - ]: IsPathRequired extends true - ? ObtainRawDocumentPathType - : ObtainRawDocumentPathType | null; + K in keyof (RequiredPaths & + OptionalPaths) + ]: IsPathRequired extends true + ? ObtainRawDocumentPathType + : ObtainRawDocumentPathType | null; }, TSchemaOptions>>; /** @@ -34,7 +35,8 @@ declare module 'mongoose' { */ type ObtainRawDocumentPathType< PathValueType, - TypeKey extends string = DefaultTypeKey + TypeKey extends string = DefaultTypeKey, + TTransformOptions = { bufferToBinary: false } > = ResolveRawPathType< PathValueType extends PathWithTypePropertyBaseType ? PathValueType[TypeKey] extends PathWithTypePropertyBaseType @@ -47,6 +49,7 @@ declare module 'mongoose' { : Omit : {}, TypeKey, + TTransformOptions, RawDocTypeHint >; @@ -63,44 +66,44 @@ declare module 'mongoose' { * @param {TypeKey} TypeKey A generic of literal string type."Refers to the property used for path type definition". * @returns Number, "Number" or "number" will be resolved to number type. */ - type ResolveRawPathType = {}, TypeKey extends string = DefaultSchemaOptions['typeKey'], TypeHint = never> = + type ResolveRawPathType = {}, TypeKey extends string = DefaultSchemaOptions['typeKey'], TTransformOptions = { bufferToBinary: false }, TypeHint = never> = IfEquals ? - IsItRecordAndNotAny extends true ? RawDocType : InferRawDocType : + IsItRecordAndNotAny extends true ? RawDocType : InferRawDocType : PathValueType extends (infer Item)[] ? IfEquals ? // If Item is a schema, infer its type. - Array extends true ? RawDocType : InferRawDocType> : + Array extends true ? RawDocType : InferRawDocType> : Item extends Record ? Item[TypeKey] extends Function | String ? // If Item has a type key that's a string or a callable, it must be a scalar, // so we can directly obtain its path type. - ObtainRawDocumentPathType[] : + ObtainRawDocumentPathType[] : // If the type key isn't callable, then this is an array of objects, in which case // we need to call InferRawDocType to correctly infer its type. - Array> : + Array> : IsSchemaTypeFromBuiltinClass extends true ? - ObtainRawDocumentPathType[] : + ObtainRawDocumentPathType[] : IsItRecordAndNotAny extends true ? Item extends Record ? - ObtainRawDocumentPathType[] : - Array> : - ObtainRawDocumentPathType[] + ObtainRawDocumentPathType[] : + Array> : + ObtainRawDocumentPathType[] >: PathValueType extends ReadonlyArray ? IfEquals ? - Array extends true ? RawDocType : InferRawDocType> : + Array extends true ? RawDocType : InferRawDocType> : Item extends Record ? Item[TypeKey] extends Function | String ? - ObtainRawDocumentPathType[] : - InferRawDocType[]: + ObtainRawDocumentPathType[] : + InferRawDocType[]: IsSchemaTypeFromBuiltinClass extends true ? - ObtainRawDocumentPathType[] : + ObtainRawDocumentPathType[] : IsItRecordAndNotAny extends true ? Item extends Record ? - ObtainRawDocumentPathType[] : - Array> : - ObtainRawDocumentPathType[] + ObtainRawDocumentPathType[] : + Array> : + ObtainRawDocumentPathType[] >: PathValueType extends StringSchemaDefinition ? PathEnumOrString : IfEquals extends true ? PathEnumOrString : @@ -109,7 +112,7 @@ declare module 'mongoose' { IfEquals extends true ? number : PathValueType extends DateSchemaDefinition ? NativeDate : IfEquals extends true ? NativeDate : - PathValueType extends typeof Buffer | 'buffer' | 'Buffer' | typeof Schema.Types.Buffer ? Buffer : + PathValueType extends typeof Buffer | 'buffer' | 'Buffer' | typeof Schema.Types.Buffer ? TTransformOptions extends { bufferToBinary: true } ? Binary : Buffer : PathValueType extends BooleanSchemaDefinition ? boolean : IfEquals extends true ? boolean : PathValueType extends ObjectIdSchemaDefinition ? Types.ObjectId : @@ -123,7 +126,7 @@ declare module 'mongoose' { PathValueType extends 'bigint' | 'BigInt' | typeof Schema.Types.BigInt | typeof BigInt ? bigint : PathValueType extends 'uuid' | 'UUID' | typeof Schema.Types.UUID ? UUID : PathValueType extends 'double' | 'Double' | typeof Schema.Types.Double ? Types.Double : - IfEquals extends true ? Buffer : + IfEquals extends true ? UUID : PathValueType extends MapConstructor | 'Map' ? Record | undefined> : IfEquals extends true ? Record | undefined> : PathValueType extends ArrayConstructor ? any[] : @@ -131,7 +134,7 @@ declare module 'mongoose' { IfEquals extends true ? any: IfEquals extends true ? any: PathValueType extends typeof SchemaType ? PathValueType['prototype'] : - PathValueType extends Record ? InferRawDocType : + PathValueType extends Record ? InferRawDocType : unknown, TypeHint>; } From db76c6462c7a4b1e45989c72c61f685ce38f3290 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Mon, 7 Jul 2025 11:57:37 -0400 Subject: [PATCH 03/18] fix tests --- test/types/schema.create.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/types/schema.create.test.ts b/test/types/schema.create.test.ts index 976a9395af4..244634cecd6 100644 --- a/test/types/schema.create.test.ts +++ b/test/types/schema.create.test.ts @@ -1876,6 +1876,6 @@ function testInferRawDocTypeFromSchema() { arr: number[], docArr: ({ name: string } & { _id: Types.ObjectId })[], subdoc?: ({ answer: number } & { _id: Types.ObjectId }) | null | undefined, - map?: Map | null | undefined + map?: Record | null | undefined } & { _id: Types.ObjectId }>({} as RawDocType); } From 4cd119fbce5c3601fb0af7bc5522b22e40295a09 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Mon, 7 Jul 2025 13:36:33 -0400 Subject: [PATCH 04/18] merge fixes --- test/types/schema.create.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/types/schema.create.test.ts b/test/types/schema.create.test.ts index 5bd306b811e..74f516c707c 100644 --- a/test/types/schema.create.test.ts +++ b/test/types/schema.create.test.ts @@ -1877,5 +1877,5 @@ function testInferRawDocTypeFromSchema() { docArr:({ name: string } & { _id: Types.ObjectId })[], subdoc?: ({ answer: number } & { _id: Types.ObjectId }) | null | undefined, map?: Record | null | undefined - } & { _id: Types.ObjectId }>({} as RawDocType); + } & { _id: Types.ObjectId }>({} as RawDocType); } From f21175f047939ad1a32f23e382326f8d8c1ccc93 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Fri, 11 Jul 2025 12:52:13 -0400 Subject: [PATCH 05/18] merge conflict cleanup --- test/types/schema.create.test.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/test/types/schema.create.test.ts b/test/types/schema.create.test.ts index 0d427ebd0b7..a839860e0a6 100644 --- a/test/types/schema.create.test.ts +++ b/test/types/schema.create.test.ts @@ -1877,11 +1877,7 @@ function testInferRawDocTypeFromSchema() { arr: number[], docArr: ({ name: string } & { _id: Types.ObjectId })[], subdoc?: ({ answer: number } & { _id: Types.ObjectId }) | null | undefined, -<<<<<<< HEAD - map?: Record | null | undefined - } & { _id: Types.ObjectId }>({} as RawDocType); -======= - map?: Map | null | undefined + map?: Record | null | undefined; } & { _id: Types.ObjectId }; expectType({} as RawDocType); @@ -1908,5 +1904,4 @@ async function testInferHydratedDocTypeFromSchema() { } & { _id: Types.ObjectId }>; expectType({} as HydratedDocType); ->>>>>>> vkarpov15/schema-create } From c30375de09e514804e1ffdc58496d596d5d08fa4 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Mon, 13 Oct 2025 16:28:56 -0400 Subject: [PATCH 06/18] some merge conflict fixes --- test/types/lean.test.ts | 7 +++---- types/document.d.ts | 2 ++ types/index.d.ts | 1 + types/inferhydrateddoctype.d.ts | 2 +- types/inferrawdoctype.d.ts | 2 +- types/models.d.ts | 20 ++++++++++++++++---- types/query.d.ts | 4 ++-- 7 files changed, 26 insertions(+), 12 deletions(-) diff --git a/test/types/lean.test.ts b/test/types/lean.test.ts index c13d4081e0d..b8a448681ed 100644 --- a/test/types/lean.test.ts +++ b/test/types/lean.test.ts @@ -1,4 +1,4 @@ -import { Schema, model, Types, InferSchemaType, FlattenMaps, HydratedDocument, Model, Document, PopulatedDoc } from 'mongoose'; +import mongoose, { Schema, model, Types, InferSchemaType, FlattenMaps, HydratedDocument, Model, Document, PopulatedDoc } from 'mongoose'; import { expectAssignable, expectError, expectType } from 'tsd'; function gh10345() { @@ -47,12 +47,11 @@ async function gh11761() { console.log({ _id, thing1 }); } - // stretch goal, make sure lean works as well const foundDoc = await ThingModel.findOne().lean().limit(1).exec(); { if (!foundDoc) { - return; // Tell TS that it isn't null + return; } const { _id, ...thing2 } = foundDoc; expectType(foundDoc._id); @@ -159,7 +158,7 @@ async function gh13010_1() { }); const country = await CountryModel.findOne().lean().orFail().exec(); - expectType>(country.name); + expectType>(country.name); } async function gh13345_1() { diff --git a/types/document.d.ts b/types/document.d.ts index 0e77d6f88e8..9841543b4b6 100644 --- a/types/document.d.ts +++ b/types/document.d.ts @@ -350,6 +350,8 @@ declare module 'mongoose' { // Default - no special options, just Require_id toObject(options?: ToObjectOptions): Require_id; + toObject(options?: ToObjectOptions): Default__v, ResolveSchemaOptions>; + /** Clears the modified state on the specified path. */ unmarkModified(path: T): void; unmarkModified(path: string): void; diff --git a/types/index.d.ts b/types/index.d.ts index 3d2ab022de1..410253eb53c 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -223,6 +223,7 @@ declare module 'mongoose' { ObtainSchemaGeneric & ObtainSchemaGeneric, ObtainSchemaGeneric, ObtainSchemaGeneric, + InferSchemaType, ObtainSchemaGeneric >; diff --git a/types/inferhydrateddoctype.d.ts b/types/inferhydrateddoctype.d.ts index 68580a96c89..ead9ea77b7f 100644 --- a/types/inferhydrateddoctype.d.ts +++ b/types/inferhydrateddoctype.d.ts @@ -106,7 +106,7 @@ declare module 'mongoose' { : PathValueType extends UuidSchemaDefinition ? UUID : PathValueType extends DoubleSchemaDefinition ? Types.Double : PathValueType extends typeof Schema.Types.Mixed ? any - : PathValueType extends MapSchemaDefinition ? Map> + : PathValueType extends MapSchemaDefinition ? Map> : IfEquals extends true ? any : PathValueType extends typeof SchemaType ? PathValueType['prototype'] : PathValueType extends ArrayConstructor ? Types.Array diff --git a/types/inferrawdoctype.d.ts b/types/inferrawdoctype.d.ts index cd987a0a464..e8fceb5bcfb 100644 --- a/types/inferrawdoctype.d.ts +++ b/types/inferrawdoctype.d.ts @@ -115,7 +115,7 @@ declare module 'mongoose' { : PathValueType extends Decimal128SchemaDefinition ? Types.Decimal128 : PathValueType extends BigintSchemaDefinition ? bigint : PathValueType extends UuidSchemaDefinition ? Types.UUID - : PathValueType extends MapSchemaDefinition ? Map> + : PathValueType extends MapSchemaDefinition ? Record> : PathValueType extends DoubleSchemaDefinition ? Types.Double : PathValueType extends UnionSchemaDefinition ? ResolveRawPathType ? Item : never> diff --git a/types/models.d.ts b/types/models.d.ts index d73d4f23c1c..edf10ee1530 100644 --- a/types/models.d.ts +++ b/types/models.d.ts @@ -476,7 +476,7 @@ declare module 'mongoose' { projection?: ProjectionType | null, options?: QueryOptions | null ): QueryWithHelpers< - HasLeanOption extends true ? TRawDocType | null : ResultDoc | null, + HasLeanOption extends true ? TLeanResultType | null : ResultDoc | null, ResultDoc, TQueryHelpers, TLeanResultType, @@ -490,7 +490,7 @@ declare module 'mongoose' { projection?: ProjectionType | null, options?: QueryOptions & mongodb.Abortable | null ): QueryWithHelpers< - HasLeanOption extends true ? TRawDocType | null : ResultDoc | null, + HasLeanOption extends true ? TLeanResultType | null : ResultDoc | null, ResultDoc, TQueryHelpers, TLeanResultType, @@ -700,12 +700,24 @@ declare module 'mongoose' { >; /** Creates a `find` query: gets a list of documents that match `filter`. */ + find( + filter: QueryFilter, + projection: ProjectionType | null | undefined, + options: QueryOptions & { lean: true } & mongodb.Abortable + ): QueryWithHelpers< + GetLeanResultType, + ResultDoc, + TQueryHelpers, + TLeanResultType, + 'find', + TInstanceMethods & TVirtuals + >; find( filter?: QueryFilter, projection?: ProjectionType | null | undefined, - options?: QueryOptions & { lean: true } & mongodb.Abortable + options?: QueryOptions & mongodb.Abortable ): QueryWithHelpers< - GetLeanResultType, + ResultDoc[], ResultDoc, TQueryHelpers, TLeanResultType, diff --git a/types/query.d.ts b/types/query.d.ts index 0b0e5d97763..98a43ce881a 100644 --- a/types/query.d.ts +++ b/types/query.d.ts @@ -522,7 +522,7 @@ declare module 'mongoose' { QueryOp, TDocOverrides >; - lean(): QueryWithHelpers< + lean(): QueryWithHelpers< ResultType extends null ? LeanResultType | null : LeanResultType, @@ -532,7 +532,7 @@ declare module 'mongoose' { QueryOp, TDocOverrides >; - lean( + lean( val: boolean | LeanOptions ): QueryWithHelpers< ResultType extends null From 3b9b6c8384e6e7910667567fc286b7771d456378 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Mon, 13 Oct 2025 16:32:54 -0400 Subject: [PATCH 07/18] fix THydratedDocumentType params --- types/index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/index.d.ts b/types/index.d.ts index 410253eb53c..c4a7c6c2417 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -297,7 +297,7 @@ declare module 'mongoose' { ObtainDocumentType>, ResolveSchemaOptions >, - THydratedDocumentType = HydratedDocument>, + THydratedDocumentType = HydratedDocument>, TSchemaDefinition = SchemaDefinition, RawDocType, THydratedDocumentType>, LeanResultType = IsItRecordAndNotAny extends true ? RawDocType : Default__v>>> > From 64027dcff4d1bdb988474e8c5513c113b099f9e8 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Mon, 13 Oct 2025 16:42:54 -0400 Subject: [PATCH 08/18] fix some missing overrides --- types/document.d.ts | 2 ++ types/models.d.ts | 68 +++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 65 insertions(+), 5 deletions(-) diff --git a/types/document.d.ts b/types/document.d.ts index 9841543b4b6..ca9eb473cba 100644 --- a/types/document.d.ts +++ b/types/document.d.ts @@ -304,6 +304,8 @@ declare module 'mongoose' { // Default - no special options, just Require_id toJSON(options?: ToObjectOptions): Require_id; + toJSON(options?: ToObjectOptions): Default__v, ResolveSchemaOptions>; + /** Converts this document into a plain-old JavaScript object ([POJO](https://masteringjs.io/tutorials/fundamentals/pojo)). */ // flattenMaps: false (default) cases toObject(options: ToObjectOptions & { flattenMaps: false, flattenObjectIds: true, virtuals: true, versionKey: false }): ObjectIdToString, '__v'>>; diff --git a/types/models.d.ts b/types/models.d.ts index edf10ee1530..2757c75bedd 100644 --- a/types/models.d.ts +++ b/types/models.d.ts @@ -473,7 +473,19 @@ declare module 'mongoose' { */ findById( id: any, - projection?: ProjectionType | null, + projection: ProjectionType | null | undefined, + options: QueryOptions & { lean: true } + ): QueryWithHelpers< + TLeanResultType | null, + ResultDoc, + TQueryHelpers, + TLeanResultType, + 'findOne', + TInstanceMethods & TVirtuals + >; + findById( + id?: any, + projection?: ProjectionType | null | undefined, options?: QueryOptions | null ): QueryWithHelpers< HasLeanOption extends true ? TLeanResultType | null : ResultDoc | null, @@ -485,10 +497,22 @@ declare module 'mongoose' { >; /** Finds one document. */ + findOne( + filter: QueryFilter, + projection: ProjectionType | null | undefined, + options: QueryOptions & { lean: true } & mongodb.Abortable + ): QueryWithHelpers< + TLeanResultType | null, + ResultDoc, + TQueryHelpers, + TLeanResultType, + 'findOne', + TInstanceMethods & TVirtuals + >; findOne( filter?: QueryFilter, - projection?: ProjectionType | null, - options?: QueryOptions & mongodb.Abortable | null + projection?: ProjectionType | null | undefined, + options?: QueryOptions & mongodb.Abortable | null | undefined ): QueryWithHelpers< HasLeanOption extends true ? TLeanResultType | null : ResultDoc | null, ResultDoc, @@ -727,8 +751,19 @@ declare module 'mongoose' { /** Creates a `findByIdAndDelete` query, filtering by the given `_id`. */ findByIdAndDelete( - id?: mongodb.ObjectId | any, - options?: QueryOptions & { lean: true } + id: mongodb.ObjectId | any, + options: QueryOptions & { includeResultMetadata: true, lean: true } + ): QueryWithHelpers< + ModifyResult, + ResultDoc, + TQueryHelpers, + TLeanResultType, + 'findOneAndDelete', + TInstanceMethods & TVirtuals + >; + findByIdAndDelete( + id: mongodb.ObjectId | any, + options: QueryOptions & { lean: true } ): QueryWithHelpers< TLeanResultType | null, ResultDoc, @@ -737,6 +772,29 @@ declare module 'mongoose' { 'findOneAndDelete', TInstanceMethods & TVirtuals >; + findByIdAndDelete( + id: mongodb.ObjectId | any, + options: QueryOptions & { includeResultMetadata: true } + ): QueryWithHelpers< + HasLeanOption extends true ? ModifyResult : ModifyResult, + ResultDoc, + TQueryHelpers, + TLeanResultType, + 'findOneAndDelete', + TInstanceMethods & TVirtuals + >; + findByIdAndDelete( + id?: mongodb.ObjectId | any, + options?: QueryOptions | null + ): QueryWithHelpers< + HasLeanOption extends true ? TLeanResultType | null : ResultDoc | null, + ResultDoc, + TQueryHelpers, + TLeanResultType, + 'findOneAndDelete', + TInstanceMethods & TVirtuals + >; + /** Creates a `findOneAndUpdate` query, filtering by the given `_id`. */ findByIdAndUpdate( From 72eba8c4368289dd7a3366e8253d486fa5a9f8d3 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Mon, 13 Oct 2025 16:50:50 -0400 Subject: [PATCH 09/18] fix: clean up a couple of more merge conflict issues --- types/document.d.ts | 8 ++++---- types/index.d.ts | 4 +++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/types/document.d.ts b/types/document.d.ts index ca9eb473cba..c848aa54725 100644 --- a/types/document.d.ts +++ b/types/document.d.ts @@ -301,8 +301,8 @@ declare module 'mongoose' { // Handle virtuals: true toJSON(options: ToObjectOptions & { virtuals: true }): Require_id; - // Default - no special options, just Require_id - toJSON(options?: ToObjectOptions): Require_id; + // Default - no special options + toJSON(options?: ToObjectOptions): Default__v, TSchemaOptions>; toJSON(options?: ToObjectOptions): Default__v, ResolveSchemaOptions>; @@ -349,8 +349,8 @@ declare module 'mongoose' { // Handle virtuals: true toObject(options: ToObjectOptions & { virtuals: true }): Require_id; - // Default - no special options, just Require_id - toObject(options?: ToObjectOptions): Require_id; + // Default - no special options + toObject(options?: ToObjectOptions): Default__v, TSchemaOptions>; toObject(options?: ToObjectOptions): Default__v, ResolveSchemaOptions>; diff --git a/types/index.d.ts b/types/index.d.ts index c4a7c6c2417..f4385db7a5c 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -98,7 +98,9 @@ declare module 'mongoose' { InferSchemaType, ObtainSchemaGeneric & ObtainSchemaGeneric, ObtainSchemaGeneric, - ObtainSchemaGeneric + ObtainSchemaGeneric, + InferSchemaType, + ObtainSchemaGeneric >, TSchema, ObtainSchemaGeneric From 31778f137e797e4209e670316b115c765db2b0ca Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Mon, 13 Oct 2025 17:03:19 -0400 Subject: [PATCH 10/18] couple of more fixes --- types/index.d.ts | 2 +- types/inferrawdoctype.d.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/types/index.d.ts b/types/index.d.ts index f4385db7a5c..8f91a145cde 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -299,7 +299,7 @@ declare module 'mongoose' { ObtainDocumentType>, ResolveSchemaOptions >, - THydratedDocumentType = HydratedDocument>, + THydratedDocumentType = HydratedDocument>, TSchemaDefinition = SchemaDefinition, RawDocType, THydratedDocumentType>, LeanResultType = IsItRecordAndNotAny extends true ? RawDocType : Default__v>>> > diff --git a/types/inferrawdoctype.d.ts b/types/inferrawdoctype.d.ts index e8fceb5bcfb..2cb845a1aa4 100644 --- a/types/inferrawdoctype.d.ts +++ b/types/inferrawdoctype.d.ts @@ -109,7 +109,7 @@ declare module 'mongoose' { Options['enum'][number] : number : PathValueType extends DateSchemaDefinition ? NativeDate - : PathValueType extends BufferSchemaDefinition ? Buffer + : PathValueType extends BufferSchemaDefinition ? (TTransformOptions extends { bufferToBinary: true } ? Binary : Buffer) : PathValueType extends BooleanSchemaDefinition ? boolean : PathValueType extends ObjectIdSchemaDefinition ? Types.ObjectId : PathValueType extends Decimal128SchemaDefinition ? Types.Decimal128 From d565fb16c8fc59053ea3decb000dfb050fb9dc46 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Mon, 13 Oct 2025 17:29:09 -0400 Subject: [PATCH 11/18] quick test fix --- test/types/schema.create.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/types/schema.create.test.ts b/test/types/schema.create.test.ts index 35238487a1b..8a79381adff 100644 --- a/test/types/schema.create.test.ts +++ b/test/types/schema.create.test.ts @@ -1177,7 +1177,7 @@ function maps() { expectType(doc.myMap!.get('answer')); const obj = doc.toObject(); - expectType>(obj.myMap); + expectType>(obj.myMap); } function gh13514() { From 0c7ce68d42fb973e8c4ba6d65546d1662ec154c1 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Mon, 13 Oct 2025 17:41:33 -0400 Subject: [PATCH 12/18] clean up a couple of more type issues --- types/inferhydrateddoctype.d.ts | 2 +- types/inferrawdoctype.d.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/types/inferhydrateddoctype.d.ts b/types/inferhydrateddoctype.d.ts index ead9ea77b7f..9d1b391d3dc 100644 --- a/types/inferhydrateddoctype.d.ts +++ b/types/inferhydrateddoctype.d.ts @@ -78,7 +78,7 @@ declare module 'mongoose' { Types.DocumentArray, Types.Subdocument['_id'], unknown, InferHydratedDocType> & InferHydratedDocType> : Item extends Record ? Item[TypeKey] extends Function | String ? - Types.Array> : + Types.Array> : Types.DocumentArray< InferRawDocType, Types.Subdocument['_id'], unknown, InferHydratedDocType> & InferHydratedDocType diff --git a/types/inferrawdoctype.d.ts b/types/inferrawdoctype.d.ts index 2cb845a1aa4..cfbcfc228b9 100644 --- a/types/inferrawdoctype.d.ts +++ b/types/inferrawdoctype.d.ts @@ -96,7 +96,7 @@ declare module 'mongoose' { : // If the type key isn't callable, then this is an array of objects, in which case // we need to call InferRawDocType to correctly infer its type. Array> - : IsSchemaTypeFromBuiltinClass extends true ? ResolveRawPathType[] + : IsSchemaTypeFromBuiltinClass extends true ? ObtainRawDocumentPathType[] : IsItRecordAndNotAny extends true ? Item extends Record ? ObtainRawDocumentPathType[] @@ -115,7 +115,7 @@ declare module 'mongoose' { : PathValueType extends Decimal128SchemaDefinition ? Types.Decimal128 : PathValueType extends BigintSchemaDefinition ? bigint : PathValueType extends UuidSchemaDefinition ? Types.UUID - : PathValueType extends MapSchemaDefinition ? Record> + : PathValueType extends MapSchemaDefinition ? Record> : PathValueType extends DoubleSchemaDefinition ? Types.Double : PathValueType extends UnionSchemaDefinition ? ResolveRawPathType ? Item : never> From b579111056062404dd05792fb14ac8060849a4dd Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Mon, 13 Oct 2025 17:49:30 -0400 Subject: [PATCH 13/18] fix enum handling in InferRawDocType --- types/inferrawdoctype.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/inferrawdoctype.d.ts b/types/inferrawdoctype.d.ts index cfbcfc228b9..9e54ef3ea0e 100644 --- a/types/inferrawdoctype.d.ts +++ b/types/inferrawdoctype.d.ts @@ -96,7 +96,7 @@ declare module 'mongoose' { : // If the type key isn't callable, then this is an array of objects, in which case // we need to call InferRawDocType to correctly infer its type. Array> - : IsSchemaTypeFromBuiltinClass extends true ? ObtainRawDocumentPathType[] + : IsSchemaTypeFromBuiltinClass extends true ? ResolveRawPathType[] : IsItRecordAndNotAny extends true ? Item extends Record ? ObtainRawDocumentPathType[] From 47bb200453c2482fba6e45a419cb39530f6d7564 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Mon, 13 Oct 2025 18:07:05 -0400 Subject: [PATCH 14/18] fix out of date test --- test/types/document.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/types/document.test.ts b/test/types/document.test.ts index 1af69d330f2..ff59cf321d1 100644 --- a/test/types/document.test.ts +++ b/test/types/document.test.ts @@ -562,7 +562,7 @@ async function gh15578() { const schemaOptions = { versionKey: 'taco' } as const; - type ModelType = Model>; + type ModelType = Model>; const ASchema = new Schema({ testProperty: Number From 5671e58fcfb5140f81f8fac45e94cae27a80c083 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Wed, 15 Oct 2025 19:07:15 -0400 Subject: [PATCH 15/18] fix up test inconsistency --- test/types/schema.create.test.ts | 40 ++++++++++++++++++++++++++------ types/index.d.ts | 2 +- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/test/types/schema.create.test.ts b/test/types/schema.create.test.ts index 8a79381adff..a525d6846ba 100644 --- a/test/types/schema.create.test.ts +++ b/test/types/schema.create.test.ts @@ -1898,13 +1898,39 @@ async function testInferHydratedDocTypeFromSchema() { type HydratedDocType = InferHydratedDocTypeFromSchema; - type Expected = HydratedDocument<{ - name?: string | null | undefined, - arr: Types.Array, - docArr: Types.DocumentArray<{ name: string } & { _id: Types.ObjectId }>, - subdoc?: HydratedDocument<{ answer: number } & { _id: Types.ObjectId }> | null | undefined, - map?: Map | null | undefined - } & { _id: Types.ObjectId }>; + type Expected = HydratedDocument< + { + name?: string | null | undefined, + arr: Types.Array, + docArr: Types.DocumentArray<{ name: string } & { _id: Types.ObjectId }>, + subdoc?: HydratedDocument<{ answer: number } & { _id: Types.ObjectId }> | null | undefined, + map?: Map | null | undefined + } & { _id: Types.ObjectId }, + {}, + {}, + {}, + { + name?: string | null | undefined, + arr: number[], + docArr: Array<{ name: string } & { _id: Types.ObjectId }>, + subdoc?: ({ answer: number } & { _id: Types.ObjectId }) | null | undefined, + map?: Record | null | undefined + } & { _id: Types.ObjectId } + >; expectType({} as HydratedDocType); + + const def = { + name: String, + arr: [Number], + docArr: [{ name: { type: String, required: true } }], + map: { type: Map, of: String } + } as const; + type InferredHydratedDocType = InferHydratedDocType; + expectType<{ + name?: string | null | undefined, + arr?: Types.Array | null | undefined, + docArr?: Types.DocumentArray<{ name: string } & { _id: Types.ObjectId }> | null | undefined, + map?: Map | null | undefined + } & { _id: Types.ObjectId }>({} as InferredHydratedDocType); } diff --git a/types/index.d.ts b/types/index.d.ts index 8f91a145cde..e326869e56d 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -367,7 +367,7 @@ declare module 'mongoose' { InferRawDocType>, ResolveSchemaOptions >, - THydratedDocumentType extends AnyObject = HydratedDocument>> + THydratedDocumentType extends AnyObject = HydratedDocument>> >(def: TSchemaDefinition, options: TSchemaOptions): Schema< RawDocType, Model, From 659c098b8050804972627f26641772d42faf6f54 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Thu, 16 Oct 2025 11:50:13 -0400 Subject: [PATCH 16/18] types: clean up a couple of more test failures due to missing TVirtuals --- test/types/schema.create.test.ts | 6 +++--- types/connection.d.ts | 29 +++++++++++++++++++---------- types/index.d.ts | 2 +- types/schemaoptions.d.ts | 2 +- 4 files changed, 24 insertions(+), 15 deletions(-) diff --git a/test/types/schema.create.test.ts b/test/types/schema.create.test.ts index a525d6846ba..64920468885 100644 --- a/test/types/schema.create.test.ts +++ b/test/types/schema.create.test.ts @@ -415,8 +415,8 @@ export function autoTypedSchema() { objectId2?: Types.ObjectId | null; objectId3?: Types.ObjectId | null; customSchema?: Int8 | null; - map1?: Record | null; - map2?: Record | null; + map1?: Record | null; + map2?: Record | null; array1: string[]; array2: any[]; array3: any[]; @@ -1880,7 +1880,7 @@ function testInferRawDocTypeFromSchema() { arr: number[], docArr: ({ name: string } & { _id: Types.ObjectId })[], subdoc?: ({ answer: number } & { _id: Types.ObjectId }) | null | undefined, - map?: Record | null | undefined; + map?: Record | null | undefined; } & { _id: Types.ObjectId }; expectType({} as RawDocType); diff --git a/types/connection.d.ts b/types/connection.d.ts index 76b36d17e6f..0581a5e80b4 100644 --- a/types/connection.d.ts +++ b/types/connection.d.ts @@ -186,16 +186,25 @@ declare module 'mongoose' { collection?: string, options?: CompileModelOptions ): Model< - InferSchemaType, - ObtainSchemaGeneric, - ObtainSchemaGeneric, - {}, - HydratedDocument< - InferSchemaType, - ObtainSchemaGeneric, - ObtainSchemaGeneric - >, - TSchema> & ObtainSchemaGeneric; + InferSchemaType, + ObtainSchemaGeneric, + ObtainSchemaGeneric, + ObtainSchemaGeneric, + // If first schema generic param is set, that means we have an explicit raw doc type, + // so user should also specify a hydrated doc type if the auto inferred one isn't correct. + IsItRecordAndNotAny> extends true + ? ObtainSchemaGeneric + : HydratedDocument< + InferSchemaType, + ObtainSchemaGeneric & ObtainSchemaGeneric, + ObtainSchemaGeneric, + ObtainSchemaGeneric, + InferSchemaType, + ObtainSchemaGeneric + >, + TSchema, + ObtainSchemaGeneric + > & ObtainSchemaGeneric; model( name: string, schema?: Schema, diff --git a/types/index.d.ts b/types/index.d.ts index e326869e56d..372453b23b6 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -299,7 +299,7 @@ declare module 'mongoose' { ObtainDocumentType>, ResolveSchemaOptions >, - THydratedDocumentType = HydratedDocument>, + THydratedDocumentType = HydratedDocument & TInstanceMethods, TQueryHelpers, TVirtuals, RawDocType, ResolveSchemaOptions>, TSchemaDefinition = SchemaDefinition, RawDocType, THydratedDocumentType>, LeanResultType = IsItRecordAndNotAny extends true ? RawDocType : Default__v>>> > diff --git a/types/schemaoptions.d.ts b/types/schemaoptions.d.ts index fefc5bd9875..a515afb8ad1 100644 --- a/types/schemaoptions.d.ts +++ b/types/schemaoptions.d.ts @@ -16,7 +16,7 @@ declare module 'mongoose' { QueryHelpers = {}, TStaticMethods = {}, TVirtuals = {}, - THydratedDocumentType = HydratedDocument, + THydratedDocumentType = HydratedDocument, TModelType = Model > { /** From d2b0ace3902b7bc0e4dd098077fa3dfc6044eb45 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Thu, 16 Oct 2025 13:24:12 -0400 Subject: [PATCH 17/18] use RawDocType in default THydratedDocumentType only if not any --- types/index.d.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/types/index.d.ts b/types/index.d.ts index 372453b23b6..ca79e022fc2 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -299,7 +299,14 @@ declare module 'mongoose' { ObtainDocumentType>, ResolveSchemaOptions >, - THydratedDocumentType = HydratedDocument & TInstanceMethods, TQueryHelpers, TVirtuals, RawDocType, ResolveSchemaOptions>, + THydratedDocumentType = HydratedDocument< + DocType, + AddDefaultId & TInstanceMethods, + TQueryHelpers, + TVirtuals, + IsItRecordAndNotAny extends true ? RawDocType : DocType, + ResolveSchemaOptions + >, TSchemaDefinition = SchemaDefinition, RawDocType, THydratedDocumentType>, LeanResultType = IsItRecordAndNotAny extends true ? RawDocType : Default__v>>> > From 4345355055c86ff33593e18cdcea17361da8f15b Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Thu, 16 Oct 2025 14:45:34 -0400 Subject: [PATCH 18/18] apply id virtual correctly and fix remaining tests --- package.json | 2 +- test/types/schema.test.ts | 4 ++-- types/index.d.ts | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 33ed8c79998..c574979d12f 100644 --- a/package.json +++ b/package.json @@ -94,7 +94,7 @@ "test": "mocha --exit ./test/*.test.js", "test-deno": "deno run --allow-env --allow-read --allow-net --allow-run --allow-sys --allow-write ./test/deno.mjs", "test-rs": "START_REPLICA_SET=1 mocha --timeout 30000 --exit ./test/*.test.js", - "test-tsd": "node ./test/types/check-types-filename && tsd", + "test-tsd": "node ./test/types/check-types-filename && tsd --full", "setup-test-encryption": "node scripts/setup-encryption-tests.js", "test-encryption": "mocha --exit ./test/encryption/*.test.js", "tdd": "mocha ./test/*.test.js --inspect --watch --recursive --watch-files ./**/*.{js,ts}", diff --git a/test/types/schema.test.ts b/test/types/schema.test.ts index 2627bd87538..f3c724997d5 100644 --- a/test/types/schema.test.ts +++ b/test/types/schema.test.ts @@ -2007,13 +2007,13 @@ function testInferHydratedDocTypeFromSchema() { type HydratedDocType = InferHydratedDocTypeFromSchema; - type Expected = HydratedDocument, subdoc?: { answer: number } | null | undefined, map?: Map | null | undefined - }>>; + }, { id: string }, {}, { id: string }>; expectType({} as HydratedDocType); } diff --git a/types/index.d.ts b/types/index.d.ts index ca79e022fc2..58f2d6e996a 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -172,7 +172,7 @@ declare module 'mongoose' { TQueryHelpers = {}, TVirtuals = {}, RawDocType = HydratedDocPathsType, - TSchemaOptions = {} + TSchemaOptions = DefaultSchemaOptions > = IfAny< HydratedDocPathsType, any, @@ -303,7 +303,7 @@ declare module 'mongoose' { DocType, AddDefaultId & TInstanceMethods, TQueryHelpers, - TVirtuals, + AddDefaultId, IsItRecordAndNotAny extends true ? RawDocType : DocType, ResolveSchemaOptions >,