From cb6d61f219b495f47428f8a45eb22c0d02c1152d Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Tue, 26 Sep 2023 15:59:28 -0400 Subject: [PATCH 1/5] wip: inferRawDocType re: #13772 --- test/types/schema.test.ts | 11 +++ types/index.d.ts | 1 + types/inferrawdoctype.d.ts | 169 +++++++++++++++++++++++++++++++++++++ 3 files changed, 181 insertions(+) create mode 100644 types/inferrawdoctype.d.ts diff --git a/test/types/schema.test.ts b/test/types/schema.test.ts index fc0204e2a71..0b5a823a1a5 100644 --- a/test/types/schema.test.ts +++ b/test/types/schema.test.ts @@ -4,6 +4,7 @@ import { HydratedDocument, IndexDefinition, IndexOptions, + InferRawDocType, InferSchemaType, InsertManyOptions, ObtainDocumentType, @@ -1230,3 +1231,13 @@ async function gh13797() { expectType(this); return ''; } } }); } + +function gh13772() { + const schemaDefinition = { + name: String, + docArr: [{ name: String }] + }; + const schema = new Schema(schemaDefinition); + type RawDocType = InferRawDocType; + expectAssignable<{ name?: string, docArr?: Array<{ name?: string }> }>({} as RawDocType); +} diff --git a/types/index.d.ts b/types/index.d.ts index 1bd4b0a7085..ce84f9100ad 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -20,6 +20,7 @@ /// /// /// +/// /// /// /// diff --git a/types/inferrawdoctype.d.ts b/types/inferrawdoctype.d.ts new file mode 100644 index 00000000000..cb4e2ea3c5a --- /dev/null +++ b/types/inferrawdoctype.d.ts @@ -0,0 +1,169 @@ +declare module 'mongoose' { + export type InferRawDocType< + DocDefinition, + TSchemaOptions extends Record = DefaultSchemaOptions + > = { + [ + K in keyof (RequiredPaths & + OptionalPaths) + ]: ObtainRawDocumentPathType; + }; + +/** + * @summary Obtains schema Path type. + * @description Obtains Path type by separating path type from other options and calling {@link ResolvePathType} + * @param {PathValueType} PathValueType Document definition path type. + * @param {TypeKey} TypeKey A generic refers to document definition. + */ +type ObtainRawDocumentPathType< + PathValueType, + TypeKey extends string = DefaultTypeKey +> = ResolveRawPathType< + PathValueType extends PathWithTypePropertyBaseType ? PathValueType[TypeKey] : PathValueType, + PathValueType extends PathWithTypePropertyBaseType ? Omit : {}, + TypeKey +>; + +type IsPathDefaultUndefined = PathType extends { default: undefined } ? + true : + PathType extends { default: (...args: any[]) => undefined } ? + true : + false; + +/** + * @summary Checks if a document path is required or optional. + * @param {P} P Document path. + * @param {TypeKey} TypeKey A generic of literal string type."Refers to the property used for path type definition". + */ +type IsPathRequired = + P extends { required: true | [true, string | undefined] | { isRequired: true } } | ArrayConstructor | any[] + ? true + : P extends { required: boolean } + ? P extends { required: false } + ? false + : true + : P extends (Record) + ? IsPathDefaultUndefined

extends true + ? false + : true + : P extends (Record) + ? P extends { default: any } + ? IfEquals + : false + : false; + +/** + * @summary Path base type defined by using TypeKey + * @description It helps to check if a path is defined by TypeKey OR not. + * @param {TypeKey} TypeKey A literal string refers to path type property key. + */ +type PathWithTypePropertyBaseType = { [k in TypeKey]: any }; + +/** + * @summary A Utility to obtain schema's required path keys. + * @param {T} T A generic refers to document definition. + * @param {TypeKey} TypeKey A generic of literal string type."Refers to the property used for path type definition". + * @returns required paths keys of document definition. + */ +type RequiredPathKeys = { + [K in keyof T]: IsPathRequired extends true ? IfEquals : never; +}[keyof T]; + +/** + * @summary A Utility to obtain schema's required paths. + * @param {T} T A generic refers to document definition. + * @param {TypeKey} TypeKey A generic of literal string type."Refers to the property used for path type definition". + * @returns a record contains required paths with the corresponding type. + */ +type RequiredPaths = { + [K in RequiredPathKeys]: T[K]; +}; + +/** + * @summary A Utility to obtain schema's optional path keys. + * @param {T} T A generic refers to document definition. + * @param {TypeKey} TypeKey A generic of literal string type."Refers to the property used for path type definition". + * @returns optional paths keys of document definition. + */ +type OptionalPathKeys = { + [K in keyof T]: IsPathRequired extends true ? never : K; +}[keyof T]; + +/** + * @param {T} T A generic refers to string path enums. + * @returns Path enum values type as literal strings or string. + */ +type PathEnumOrString['enum']> = T extends ReadonlyArray ? E : T extends { values: any } ? PathEnumOrString : string; + +/** + * @summary A Utility to obtain schema's optional paths. + * @param {T} T A generic refers to document definition. + * @param {TypeKey} TypeKey A generic of literal string type."Refers to the property used for path type definition". + * @returns a record contains optional paths with the corresponding type. + */ +type OptionalPaths = { + [K in OptionalPathKeys]?: T[K]; +}; + +/** + * @summary Resolve path type by returning the corresponding type. + * @param {PathValueType} PathValueType Document definition path type. + * @param {Options} Options Document definition path options except path type. + * @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']> = + PathValueType extends Schema ? InferRawDocType : + PathValueType extends (infer Item)[] ? + IfEquals> : + 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[] : + // If the type key isn't callable, then this is an array of objects, in which case + // we need to call ObtainDocumentType to correctly infer its type. + InferRawDocType[]: + ObtainRawDocumentPathType[] + >: + PathValueType extends ReadonlyArray ? + IfEquals> : + Item extends Record ? + Item[TypeKey] extends Function | String ? + ObtainRawDocumentPathType[] : + InferRawDocType[]: + ObtainRawDocumentPathType[] + >: + PathValueType extends StringSchemaDefinition ? PathEnumOrString : + IfEquals extends true ? PathEnumOrString : + IfEquals extends true ? PathEnumOrString : + PathValueType extends NumberSchemaDefinition ? Options['enum'] extends ReadonlyArray ? Options['enum'][number] : number : + IfEquals extends true ? number : + PathValueType extends DateSchemaDefinition ? Date : + IfEquals extends true ? Date : + PathValueType extends typeof Buffer | 'buffer' | 'Buffer' | typeof Schema.Types.Buffer ? Buffer : + PathValueType extends BooleanSchemaDefinition ? boolean : + IfEquals extends true ? boolean : + PathValueType extends ObjectIdSchemaDefinition ? Types.ObjectId : + IfEquals extends true ? Types.ObjectId : + IfEquals extends true ? Types.ObjectId : + PathValueType extends 'decimal128' | 'Decimal128' | typeof Schema.Types.Decimal128 ? Types.Decimal128 : + IfEquals extends true ? Types.Decimal128 : + IfEquals extends true ? Types.Decimal128 : + IfEquals extends true ? bigint : + PathValueType extends 'bigint' | 'BigInt' | typeof Schema.Types.BigInt ? bigint : + PathValueType extends 'uuid' | 'UUID' | typeof Schema.Types.UUID ? Buffer : + IfEquals extends true ? Buffer : + PathValueType extends MapConstructor ? Map> : + IfEquals extends true ? Map> : + PathValueType extends ArrayConstructor ? any[] : + PathValueType extends typeof Schema.Types.Mixed ? any: + IfEquals extends true ? any: + IfEquals extends true ? any: + PathValueType extends typeof SchemaType ? PathValueType['prototype'] : + PathValueType extends Record ? ObtainDocumentType : + unknown; +} \ No newline at end of file From 7f9d520b913d5cfe68ec4b98319b940f688f5f6c Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Thu, 28 Sep 2023 16:08:30 -0400 Subject: [PATCH 2/5] refactor(inferrawdoctype): import helpers from inferschematype --- types/inferrawdoctype.d.ts | 234 +++++++++++++------------------------ 1 file changed, 80 insertions(+), 154 deletions(-) diff --git a/types/inferrawdoctype.d.ts b/types/inferrawdoctype.d.ts index cb4e2ea3c5a..49b05d2ce9f 100644 --- a/types/inferrawdoctype.d.ts +++ b/types/inferrawdoctype.d.ts @@ -1,169 +1,95 @@ +import { + RequiredPaths, + OptionalPaths, + PathWithTypePropertyBaseType, + PathEnumOrString +} from './inferschematype'; + declare module 'mongoose' { export type InferRawDocType< DocDefinition, TSchemaOptions extends Record = DefaultSchemaOptions > = { [ - K in keyof (RequiredPaths & - OptionalPaths) + K in keyof (RequiredPaths & + OptionalPaths) ]: ObtainRawDocumentPathType; }; -/** - * @summary Obtains schema Path type. - * @description Obtains Path type by separating path type from other options and calling {@link ResolvePathType} - * @param {PathValueType} PathValueType Document definition path type. - * @param {TypeKey} TypeKey A generic refers to document definition. - */ -type ObtainRawDocumentPathType< - PathValueType, - TypeKey extends string = DefaultTypeKey -> = ResolveRawPathType< - PathValueType extends PathWithTypePropertyBaseType ? PathValueType[TypeKey] : PathValueType, - PathValueType extends PathWithTypePropertyBaseType ? Omit : {}, - TypeKey ->; - -type IsPathDefaultUndefined = PathType extends { default: undefined } ? - true : - PathType extends { default: (...args: any[]) => undefined } ? - true : - false; - -/** - * @summary Checks if a document path is required or optional. - * @param {P} P Document path. - * @param {TypeKey} TypeKey A generic of literal string type."Refers to the property used for path type definition". - */ -type IsPathRequired = - P extends { required: true | [true, string | undefined] | { isRequired: true } } | ArrayConstructor | any[] - ? true - : P extends { required: boolean } - ? P extends { required: false } - ? false - : true - : P extends (Record) - ? IsPathDefaultUndefined

extends true - ? false - : true - : P extends (Record) - ? P extends { default: any } - ? IfEquals - : false - : false; - -/** - * @summary Path base type defined by using TypeKey - * @description It helps to check if a path is defined by TypeKey OR not. - * @param {TypeKey} TypeKey A literal string refers to path type property key. - */ -type PathWithTypePropertyBaseType = { [k in TypeKey]: any }; - -/** - * @summary A Utility to obtain schema's required path keys. - * @param {T} T A generic refers to document definition. - * @param {TypeKey} TypeKey A generic of literal string type."Refers to the property used for path type definition". - * @returns required paths keys of document definition. - */ -type RequiredPathKeys = { - [K in keyof T]: IsPathRequired extends true ? IfEquals : never; -}[keyof T]; - -/** - * @summary A Utility to obtain schema's required paths. - * @param {T} T A generic refers to document definition. - * @param {TypeKey} TypeKey A generic of literal string type."Refers to the property used for path type definition". - * @returns a record contains required paths with the corresponding type. - */ -type RequiredPaths = { - [K in RequiredPathKeys]: T[K]; -}; + /** + * @summary Obtains schema Path type. + * @description Obtains Path type by separating path type from other options and calling {@link ResolvePathType} + * @param {PathValueType} PathValueType Document definition path type. + * @param {TypeKey} TypeKey A generic refers to document definition. + */ + type ObtainRawDocumentPathType< + PathValueType, + TypeKey extends string = DefaultTypeKey + > = ResolveRawPathType< + PathValueType extends PathWithTypePropertyBaseType ? PathValueType[TypeKey] : PathValueType, + PathValueType extends PathWithTypePropertyBaseType ? Omit : {}, + TypeKey + >; -/** - * @summary A Utility to obtain schema's optional path keys. - * @param {T} T A generic refers to document definition. - * @param {TypeKey} TypeKey A generic of literal string type."Refers to the property used for path type definition". - * @returns optional paths keys of document definition. - */ -type OptionalPathKeys = { - [K in keyof T]: IsPathRequired extends true ? never : K; -}[keyof T]; - -/** - * @param {T} T A generic refers to string path enums. - * @returns Path enum values type as literal strings or string. - */ -type PathEnumOrString['enum']> = T extends ReadonlyArray ? E : T extends { values: any } ? PathEnumOrString : string; - -/** - * @summary A Utility to obtain schema's optional paths. - * @param {T} T A generic refers to document definition. - * @param {TypeKey} TypeKey A generic of literal string type."Refers to the property used for path type definition". - * @returns a record contains optional paths with the corresponding type. - */ -type OptionalPaths = { - [K in OptionalPathKeys]?: T[K]; -}; - -/** - * @summary Resolve path type by returning the corresponding type. - * @param {PathValueType} PathValueType Document definition path type. - * @param {Options} Options Document definition path options except path type. - * @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']> = - PathValueType extends Schema ? InferRawDocType : - PathValueType extends (infer Item)[] ? - IfEquals> : - 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[] : - // If the type key isn't callable, then this is an array of objects, in which case - // we need to call ObtainDocumentType to correctly infer its type. - InferRawDocType[]: - ObtainRawDocumentPathType[] - >: - PathValueType extends ReadonlyArray ? + /** + * @summary Resolve path type by returning the corresponding type. + * @param {PathValueType} PathValueType Document definition path type. + * @param {Options} Options Document definition path options except path type. + * @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']> = + PathValueType extends Schema ? InferRawDocType : + PathValueType extends (infer Item)[] ? IfEquals> : - Item extends Record ? + Item extends Record? Item[TypeKey] extends Function | String ? - ObtainRawDocumentPathType[] : + // 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[] : + // If the type key isn't callable, then this is an array of objects, in which case + // we need to call ObtainDocumentType to correctly infer its type. InferRawDocType[]: - ObtainRawDocumentPathType[] + ObtainRawDocumentPathType[] >: - PathValueType extends StringSchemaDefinition ? PathEnumOrString : - IfEquals extends true ? PathEnumOrString : - IfEquals extends true ? PathEnumOrString : - PathValueType extends NumberSchemaDefinition ? Options['enum'] extends ReadonlyArray ? Options['enum'][number] : number : - IfEquals extends true ? number : - PathValueType extends DateSchemaDefinition ? Date : - IfEquals extends true ? Date : - PathValueType extends typeof Buffer | 'buffer' | 'Buffer' | typeof Schema.Types.Buffer ? Buffer : - PathValueType extends BooleanSchemaDefinition ? boolean : - IfEquals extends true ? boolean : - PathValueType extends ObjectIdSchemaDefinition ? Types.ObjectId : - IfEquals extends true ? Types.ObjectId : - IfEquals extends true ? Types.ObjectId : - PathValueType extends 'decimal128' | 'Decimal128' | typeof Schema.Types.Decimal128 ? Types.Decimal128 : - IfEquals extends true ? Types.Decimal128 : - IfEquals extends true ? Types.Decimal128 : - IfEquals extends true ? bigint : - PathValueType extends 'bigint' | 'BigInt' | typeof Schema.Types.BigInt ? bigint : - PathValueType extends 'uuid' | 'UUID' | typeof Schema.Types.UUID ? Buffer : - IfEquals extends true ? Buffer : - PathValueType extends MapConstructor ? Map> : - IfEquals extends true ? Map> : - PathValueType extends ArrayConstructor ? any[] : - PathValueType extends typeof Schema.Types.Mixed ? any: - IfEquals extends true ? any: - IfEquals extends true ? any: - PathValueType extends typeof SchemaType ? PathValueType['prototype'] : - PathValueType extends Record ? ObtainDocumentType : - unknown; -} \ No newline at end of file + PathValueType extends ReadonlyArray ? + IfEquals> : + Item extends Record ? + Item[TypeKey] extends Function | String ? + ObtainRawDocumentPathType[] : + InferRawDocType[]: + ObtainRawDocumentPathType[] + >: + PathValueType extends StringSchemaDefinition ? PathEnumOrString : + IfEquals extends true ? PathEnumOrString : + IfEquals extends true ? PathEnumOrString : + PathValueType extends NumberSchemaDefinition ? Options['enum'] extends ReadonlyArray ? Options['enum'][number] : number : + IfEquals extends true ? number : + PathValueType extends DateSchemaDefinition ? Date : + IfEquals extends true ? Date : + PathValueType extends typeof Buffer | 'buffer' | 'Buffer' | typeof Schema.Types.Buffer ? Buffer : + PathValueType extends BooleanSchemaDefinition ? boolean : + IfEquals extends true ? boolean : + PathValueType extends ObjectIdSchemaDefinition ? Types.ObjectId : + IfEquals extends true ? Types.ObjectId : + IfEquals extends true ? Types.ObjectId : + PathValueType extends 'decimal128' | 'Decimal128' | typeof Schema.Types.Decimal128 ? Types.Decimal128 : + IfEquals extends true ? Types.Decimal128 : + IfEquals extends true ? Types.Decimal128 : + IfEquals extends true ? bigint : + PathValueType extends 'bigint' | 'BigInt' | typeof Schema.Types.BigInt ? bigint : + PathValueType extends 'uuid' | 'UUID' | typeof Schema.Types.UUID ? Buffer : + IfEquals extends true ? Buffer : + PathValueType extends MapConstructor ? Map> : + IfEquals extends true ? Map> : + PathValueType extends ArrayConstructor ? any[] : + PathValueType extends typeof Schema.Types.Mixed ? any: + IfEquals extends true ? any: + IfEquals extends true ? any: + PathValueType extends typeof SchemaType ? PathValueType['prototype'] : + PathValueType extends Record ? ObtainDocumentType : + unknown; +} From 713c9ba2ec153d7df8a4bb1429255d9ffb057a26 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Mon, 13 May 2024 12:18:16 -0400 Subject: [PATCH 3/5] test: fix tests --- test/types/schema.test.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/test/types/schema.test.ts b/test/types/schema.test.ts index c5ee82c0c10..bbbcdd5d71f 100644 --- a/test/types/schema.test.ts +++ b/test/types/schema.test.ts @@ -1503,5 +1503,11 @@ function gh13772() { }; const schema = new Schema(schemaDefinition); type RawDocType = InferRawDocType; - expectAssignable<{ name?: string, docArr?: Array<{ name?: string }> }>({} as RawDocType); -} \ No newline at end of file + expectAssignable< + { name?: string | null, docArr?: Array<{ name?: string | null }> } + >({} as RawDocType); + + const TestModel = model('User', schema); + const doc = new TestModel(); + expectAssignable(doc.toObject()); +} From 792bcac7f34a637e4ce308e3ab8abbbdff47c685 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Mon, 13 May 2024 12:57:29 -0400 Subject: [PATCH 4/5] adapt changes from inferSchemaType --- scripts/tsc-diagnostics-check.js | 2 +- types/inferrawdoctype.d.ts | 99 +++++++++++++++++++------------- 2 files changed, 61 insertions(+), 40 deletions(-) diff --git a/scripts/tsc-diagnostics-check.js b/scripts/tsc-diagnostics-check.js index 2f74bf39b92..3e1f6c66282 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]) ? 125000 : parseInt(process.argv[2], 10); +const maxInstantiations = isNaN(process.argv[2]) ? 127500 : parseInt(process.argv[2], 10); console.log(stdin); diff --git a/types/inferrawdoctype.d.ts b/types/inferrawdoctype.d.ts index 49b05d2ce9f..95a912ff240 100644 --- a/types/inferrawdoctype.d.ts +++ b/types/inferrawdoctype.d.ts @@ -1,4 +1,5 @@ import { + IsSchemaTypeFromBuiltinClass, RequiredPaths, OptionalPaths, PathWithTypePropertyBaseType, @@ -32,6 +33,12 @@ declare module 'mongoose' { >; /** + * Same as inferSchemaType, except: + * + * 1. Replace `Types.DocumentArray` and `Types.Array` with vanilla `Array` + * 2. Replace `ObtainDocumentPathType` with `ObtainRawDocumentPathType` + * 3. Replace `ResolvePathType` with `ResolveRawPathType` + * * @summary Resolve path type by returning the corresponding type. * @param {PathValueType} PathValueType Document definition path type. * @param {Options} Options Document definition path options except path type. @@ -39,51 +46,65 @@ declare module 'mongoose' { * @returns Number, "Number" or "number" will be resolved to number type. */ type ResolveRawPathType = {}, TypeKey extends string = DefaultSchemaOptions['typeKey']> = - PathValueType extends Schema ? InferRawDocType : - PathValueType extends (infer Item)[] ? + PathValueType extends Schema ? + InferSchemaType : + PathValueType extends (infer Item)[] ? + IfEquals> : + 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[] : + // If the type key isn't callable, then this is an array of objects, in which case + // we need to call ObtainDocumentType to correctly infer its type. + Array> : + IsSchemaTypeFromBuiltinClass extends true ? + ObtainRawDocumentPathType[] : + IsItRecordAndNotAny extends true ? + Item extends Record ? + ObtainRawDocumentPathType[] : + Array> : + ObtainRawDocumentPathType[] + >: + PathValueType extends ReadonlyArray ? IfEquals> : - Item extends Record? + Array> : + 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[] : - // If the type key isn't callable, then this is an array of objects, in which case - // we need to call ObtainDocumentType to correctly infer its type. - InferRawDocType[]: - ObtainRawDocumentPathType[] + ObtainDocumentType[]: + IsSchemaTypeFromBuiltinClass extends true ? + ObtainRawDocumentPathType[] : + IsItRecordAndNotAny extends true ? + Item extends Record ? + ObtainRawDocumentPathType[] : + Array> : + ObtainRawDocumentPathType[] >: - PathValueType extends ReadonlyArray ? - IfEquals> : - Item extends Record ? - Item[TypeKey] extends Function | String ? - ObtainRawDocumentPathType[] : - InferRawDocType[]: - ObtainRawDocumentPathType[] - >: - PathValueType extends StringSchemaDefinition ? PathEnumOrString : - IfEquals extends true ? PathEnumOrString : - IfEquals extends true ? PathEnumOrString : - PathValueType extends NumberSchemaDefinition ? Options['enum'] extends ReadonlyArray ? Options['enum'][number] : number : - IfEquals extends true ? number : - PathValueType extends DateSchemaDefinition ? Date : - IfEquals extends true ? Date : - PathValueType extends typeof Buffer | 'buffer' | 'Buffer' | typeof Schema.Types.Buffer ? Buffer : - PathValueType extends BooleanSchemaDefinition ? boolean : - IfEquals extends true ? boolean : - PathValueType extends ObjectIdSchemaDefinition ? Types.ObjectId : - IfEquals extends true ? Types.ObjectId : - IfEquals extends true ? Types.ObjectId : - PathValueType extends 'decimal128' | 'Decimal128' | typeof Schema.Types.Decimal128 ? Types.Decimal128 : - IfEquals extends true ? Types.Decimal128 : - IfEquals extends true ? Types.Decimal128 : - IfEquals extends true ? bigint : - PathValueType extends 'bigint' | 'BigInt' | typeof Schema.Types.BigInt ? bigint : + PathValueType extends StringSchemaDefinition ? PathEnumOrString : + IfEquals extends true ? PathEnumOrString : + IfEquals extends true ? PathEnumOrString : + PathValueType extends NumberSchemaDefinition ? Options['enum'] extends ReadonlyArray ? Options['enum'][number] : number : + IfEquals extends true ? number : + PathValueType extends DateSchemaDefinition ? Date : + IfEquals extends true ? Date : + PathValueType extends typeof Buffer | 'buffer' | 'Buffer' | typeof Schema.Types.Buffer ? Buffer : + PathValueType extends BooleanSchemaDefinition ? boolean : + IfEquals extends true ? boolean : + PathValueType extends ObjectIdSchemaDefinition ? Types.ObjectId : + IfEquals extends true ? Types.ObjectId : + IfEquals extends true ? Types.ObjectId : + PathValueType extends 'decimal128' | 'Decimal128' | typeof Schema.Types.Decimal128 ? Types.Decimal128 : + IfEquals extends true ? Types.Decimal128 : + IfEquals extends true ? Types.Decimal128 : + IfEquals extends true ? bigint : + IfEquals extends true ? bigint : + PathValueType extends 'bigint' | 'BigInt' | typeof Schema.Types.BigInt | typeof BigInt ? bigint : PathValueType extends 'uuid' | 'UUID' | typeof Schema.Types.UUID ? Buffer : IfEquals extends true ? Buffer : - PathValueType extends MapConstructor ? Map> : + PathValueType extends MapConstructor | 'Map' ? Map> : IfEquals extends true ? Map> : PathValueType extends ArrayConstructor ? any[] : PathValueType extends typeof Schema.Types.Mixed ? any: From 366004148dc0c7bd95d81bee28a19267042aed02 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Wed, 15 May 2024 17:59:25 -0400 Subject: [PATCH 5/5] docs(typescript): add section on InferRawDocType to TypeScript docs --- docs/typescript/schemas.md | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/docs/typescript/schemas.md b/docs/typescript/schemas.md index e4127ce1621..8dfa5310556 100644 --- a/docs/typescript/schemas.md +++ b/docs/typescript/schemas.md @@ -9,7 +9,7 @@ Mongoose can automatically infer the document type from your schema definition a We recommend relying on automatic type inference when defining schemas and models. ```typescript -import { Schema } from 'mongoose'; +import { Schema, model } from 'mongoose'; // Schema const schema = new Schema({ name: { type: String, required: true }, @@ -32,6 +32,31 @@ There are a few caveats for using automatic type inference: 2. You need to define your schema in the `new Schema()` call. Don't assign your schema definition to a temporary variable. Doing something like `const schemaDefinition = { name: String }; const schema = new Schema(schemaDefinition);` will not work. 3. Mongoose adds `createdAt` and `updatedAt` to your schema if you specify the `timestamps` option in your schema, *except* if you also specify `methods`, `virtuals`, or `statics`. There is a [known issue](https://github.com/Automattic/mongoose/issues/12807) with type inference with timestamps and methods/virtuals/statics options. If you use methods, virtuals, and statics, you're responsible for adding `createdAt` and `updatedAt` to your schema definition. +If you need to explicitly get the raw document type (the value returned from `doc.toObject()`, `await Model.findOne().lean()`, etc.) from your schema definition, you can use Mongoose's `inferRawDocType` helper as follows: + +```ts +import { Schema, InferRawDocType, model } from 'mongoose'; + +const schemaDefinition = { + name: { type: String, required: true }, + email: { type: String, required: true }, + avatar: String +} as const; +const schema = new Schema(schemaDefinition); + +const UserModel = model('User', schema); +const doc = new UserModel({ name: 'test', email: 'test' }); + +type RawUserDocument = InferRawDocType; + +useRawDoc(doc.toObject()); + +function useRawDoc(doc: RawUserDocument) { + // ... +} + +``` + If automatic type inference doesn't work for you, you can always fall back to document interface definitions. ## Separate document interface definition