From 52c75735a20853d1e65cc2f01cbf70e55b76b923 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Sat, 5 Jul 2025 14:36:29 -0400 Subject: [PATCH 1/5] 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 2/5] 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 3/5] 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 4/5] 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 5/5] 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 }