From 7d13e130dafff9646ca3bdf960e85c2b6fd1ca59 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Tue, 22 Jul 2025 14:45:58 -0400 Subject: [PATCH 1/4] types: automatically infer discriminator type Fix #15535 --- test/types/discriminator.test.ts | 40 ++++++++++++++++++++++++++++++++ types/models.d.ts | 28 +++++++++++++++++++++- 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/test/types/discriminator.test.ts b/test/types/discriminator.test.ts index fa8e9a3a1b0..62599311584 100644 --- a/test/types/discriminator.test.ts +++ b/test/types/discriminator.test.ts @@ -1,4 +1,5 @@ import mongoose, { Document, Model, Schema, SchemaDefinition, SchemaOptions, Types, model } from 'mongoose'; +import { expectType } from 'tsd'; const schema: Schema = new Schema({ name: { type: 'String' } }); @@ -76,3 +77,42 @@ function test(): void { const sampleCardDb: CardDb = sampleLandDb; } + +function gh15535() { + const ParentSchema = new Schema({ + field1: { + type: String, + required: true, + }, + field2: Number, + }, { + discriminatorKey: 'field1', + methods: { + getField2() { + return this.field2; + } + } + }); + + const ParentModel = mongoose.model('Parent', ParentSchema) + + const ChildSchema = new Schema({ + field3: String, + }, { + methods: { + getField3() { + return this.field3; + } + } + }); + + + const ChildModel = ParentModel.discriminator('child', ChildSchema) + + const doc = new ChildModel({}); + expectType(doc.field1); + expectType(doc.field2); + expectType(doc.getField2()); + expectType(doc.field3); + expectType(doc.getField3()); +} \ No newline at end of file diff --git a/types/models.d.ts b/types/models.d.ts index bc205aed74c..65ae878bafa 100644 --- a/types/models.d.ts +++ b/types/models.d.ts @@ -266,7 +266,6 @@ declare module 'mongoose' { THydratedDocumentType = HydratedDocument, TSchema = any> extends NodeJS.EventEmitter, - AcceptsDiscriminator, IndexManager, SessionStarter { new >(doc?: DocType, fields?: any | null, options?: boolean | AnyObject): THydratedDocumentType; @@ -419,6 +418,33 @@ declare module 'mongoose' { TInstanceMethods & TVirtuals >; + /** Adds a discriminator type. */ + discriminator>( + name: string, + schema: TDiscriminatorSchema, + value?: string | number | ObjectId | DiscriminatorOptions + ): Model< + TRawDocType & InferSchemaType, + TQueryHelpers & ObtainSchemaGeneric, + TVirtuals & ObtainSchemaGeneric, + HydratedDocument< + TRawDocType & InferSchemaType, + TVirtuals & ObtainSchemaGeneric & TInstanceMethods & ObtainSchemaGeneric, + TQueryHelpers & ObtainSchemaGeneric, + TVirtuals & ObtainSchemaGeneric + > + > & ObtainSchemaGeneric; + discriminator( + name: string | number, + schema: Schema, + value?: string | number | ObjectId | DiscriminatorOptions + ): Model; + discriminator( + name: string | number, + schema: Schema, + value?: string | number | ObjectId | DiscriminatorOptions + ): U; + /** * Delete an existing [Atlas search index](https://www.mongodb.com/docs/atlas/atlas-search/create-index/) by name. * This function only works when connected to MongoDB Atlas. From a24d2d3cc8ec48947a1298523be93890a3fd1846 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Tue, 22 Jul 2025 14:48:25 -0400 Subject: [PATCH 2/4] fix lint --- test/types/discriminator.test.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/types/discriminator.test.ts b/test/types/discriminator.test.ts index 62599311584..87b36d94944 100644 --- a/test/types/discriminator.test.ts +++ b/test/types/discriminator.test.ts @@ -82,9 +82,9 @@ function gh15535() { const ParentSchema = new Schema({ field1: { type: String, - required: true, + required: true }, - field2: Number, + field2: Number }, { discriminatorKey: 'field1', methods: { @@ -94,10 +94,10 @@ function gh15535() { } }); - const ParentModel = mongoose.model('Parent', ParentSchema) + const ParentModel = mongoose.model('Parent', ParentSchema); const ChildSchema = new Schema({ - field3: String, + field3: String }, { methods: { getField3() { @@ -107,7 +107,7 @@ function gh15535() { }); - const ChildModel = ParentModel.discriminator('child', ChildSchema) + const ChildModel = ParentModel.discriminator('child', ChildSchema); const doc = new ChildModel({}); expectType(doc.field1); @@ -115,4 +115,4 @@ function gh15535() { expectType(doc.getField2()); expectType(doc.field3); expectType(doc.getField3()); -} \ No newline at end of file +} From af00f5234a6a8456ddd6e506ccbf3352904c428e Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Wed, 23 Jul 2025 11:29:32 -0400 Subject: [PATCH 3/4] types: options cleanup for performance --- benchmarks/typescript/simple/package.json | 2 +- scripts/tsc-diagnostics-check.js | 2 +- types/index.d.ts | 7 +------ types/models.d.ts | 19 +++++++------------ types/query.d.ts | 18 ++++-------------- 5 files changed, 14 insertions(+), 34 deletions(-) diff --git a/benchmarks/typescript/simple/package.json b/benchmarks/typescript/simple/package.json index 6571d866d06..b936a44a756 100644 --- a/benchmarks/typescript/simple/package.json +++ b/benchmarks/typescript/simple/package.json @@ -1,7 +1,7 @@ { "dependencies": { "mongoose": "file:../../../mongoose.tgz", - "typescript": "5.5.x" + "typescript": "5.8.x" }, "scripts": { "benchmark": "tsc --extendedDiagnostics" diff --git a/scripts/tsc-diagnostics-check.js b/scripts/tsc-diagnostics-check.js index a13c884ee02..bc385cb1347 100644 --- a/scripts/tsc-diagnostics-check.js +++ b/scripts/tsc-diagnostics-check.js @@ -3,7 +3,7 @@ const fs = require('fs'); const stdin = fs.readFileSync(0).toString('utf8'); -const maxInstantiations = isNaN(process.argv[2]) ? 275000 : parseInt(process.argv[2], 10); +const maxInstantiations = isNaN(process.argv[2]) ? 280000 : parseInt(process.argv[2], 10); console.log(stdin); diff --git a/types/index.d.ts b/types/index.d.ts index 3d06255a035..5594a034492 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -602,7 +602,7 @@ declare module 'mongoose' { | typeof Schema.Types.UUID; - export type InferId = T extends { _id?: any } ? T['_id'] : Types.ObjectId; + export type InferId = mongodb.InferIdType; export interface VirtualTypeOptions { /** If `ref` is not nullish, this becomes a populated virtual. */ @@ -897,11 +897,6 @@ declare module 'mongoose' { export type SchemaDefinitionType = T extends Document ? Omit> : T; - /** - * Helper to choose the best option between two type helpers - */ - export type _pickObject = T1 extends false ? T2 extends false ? Fallback : T2 : T1; - /* for ts-mongoose */ export class mquery { } diff --git a/types/models.d.ts b/types/models.d.ts index 02038961246..6dbc8952eef 100644 --- a/types/models.d.ts +++ b/types/models.d.ts @@ -312,11 +312,11 @@ declare module 'mongoose' { * round trip to the MongoDB server. */ bulkWrite( - writes: Array>, + writes: Array>, options: MongooseBulkWriteOptions & { ordered: false } ): Promise; bulkWrite( - writes: Array>, + writes: Array>, options?: MongooseBulkWriteOptions ): Promise; @@ -426,20 +426,15 @@ declare module 'mongoose' { >; /** Adds a discriminator type. */ - discriminator>( - name: string, + discriminator>( + name: string | number, schema: TDiscriminatorSchema, value?: string | number | ObjectId | DiscriminatorOptions ): Model< TRawDocType & InferSchemaType, TQueryHelpers & ObtainSchemaGeneric, - TVirtuals & ObtainSchemaGeneric, - HydratedDocument< - TRawDocType & InferSchemaType, - TVirtuals & ObtainSchemaGeneric & TInstanceMethods & ObtainSchemaGeneric, - TQueryHelpers & ObtainSchemaGeneric, - TVirtuals & ObtainSchemaGeneric - > + TInstanceMethods & ObtainSchemaGeneric, + TVirtuals & ObtainSchemaGeneric > & ObtainSchemaGeneric; discriminator( name: string | number, @@ -911,7 +906,7 @@ declare module 'mongoose' { replaceOne( filter?: RootFilterQuery, replacement?: TRawDocType | AnyObject, - options?: (mongodb.ReplaceOptions & MongooseQueryOptions) | null + options?: (mongodb.ReplaceOptions & QueryOptions) | null ): QueryWithHelpers; /** Apply changes made to this model's schema after this model was compiled. */ diff --git a/types/query.d.ts b/types/query.d.ts index e625ec56635..5bfcd63fdcf 100644 --- a/types/query.d.ts +++ b/types/query.d.ts @@ -31,19 +31,9 @@ declare module 'mongoose' { | 'strictQuery' | 'translateAliases'; - type MongooseQueryOptions< - DocType = unknown, - Keys extends keyof QueryOptions = MongooseBaseQueryOptionKeys | 'timestamps' | 'lean' - > = Pick, Keys> & { - [other: string]: any; - }; + type MongooseBaseQueryOptions = Pick, MongooseBaseQueryOptionKeys>; - type MongooseBaseQueryOptions = MongooseQueryOptions; - - type MongooseUpdateQueryOptions = MongooseQueryOptions< - DocType, - MongooseBaseQueryOptionKeys | 'timestamps' - >; + type MongooseUpdateQueryOptions = Pick, MongooseBaseQueryOptionKeys | 'timestamps'>; type ProjectionFields = { [Key in keyof DocType]?: any } & Record; @@ -239,7 +229,7 @@ declare module 'mongoose' { : MergeType; class Query> implements SessionOperation { - _mongooseOptions: MongooseQueryOptions; + _mongooseOptions: QueryOptions; /** * Returns a wrapper around a [mongodb driver cursor](https://mongodb.github.io/node-mongodb-native/4.9/classes/FindCursor.html). @@ -634,7 +624,7 @@ declare module 'mongoose' { * Getter/setter around the current mongoose-specific options for this query * Below are the current Mongoose-specific options. */ - mongooseOptions(val?: MongooseQueryOptions): MongooseQueryOptions; + mongooseOptions(val?: QueryOptions): QueryOptions; /** Specifies a `$ne` query condition. When called with one argument, the most recent path passed to `where()` is used. */ ne(path: K, val: any): this; From 8338cf632538bf67612c06807b208e989302e9d0 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Fri, 25 Jul 2025 09:51:42 -0400 Subject: [PATCH 4/4] test: quick test fix - static Model context likely not identical due to #15532 Re: #15535 --- test/types/schema.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/types/schema.test.ts b/test/types/schema.test.ts index a19a910612b..e798d4bb50a 100644 --- a/test/types/schema.test.ts +++ b/test/types/schema.test.ts @@ -532,7 +532,7 @@ export function autoTypedSchema() { }, { statics: { staticFn() { - expectType>>(this); + expectAssignable>>(this); return 'Returned from staticFn' as const; } },