diff --git a/lib/mongoose.js b/lib/mongoose.js index acdf46ec62..3cbfbf9260 100644 --- a/lib/mongoose.js +++ b/lib/mongoose.js @@ -939,6 +939,15 @@ Mongoose.prototype.Mongoose = Mongoose; Mongoose.prototype.Schema = Schema; +/** + * Identical to `Schema` at runtime. Exists purely to work around some TypeScript compatibility issues. + * + * @method AutoInferredSchema + * @api public + */ + +Mongoose.prototype.AutoInferredSchema = Schema; + /** * The Mongoose [SchemaType](https://mongoosejs.com/docs/api/schematype.html#SchemaType()) constructor * diff --git a/test/types/inferrawdoctype.test.ts b/test/types/inferrawdoctype.test.ts index 7d162b0397..32779d0fd6 100644 --- a/test/types/inferrawdoctype.test.ts +++ b/test/types/inferrawdoctype.test.ts @@ -1,4 +1,4 @@ -import { InferRawDocType } from 'mongoose'; +import { InferRawDocType, Schema, AutoInferredSchema } from 'mongoose'; import { expectType, expectError } from 'tsd'; function gh14839() { @@ -17,9 +17,55 @@ function gh14839() { dateOfBirth: { type: Date, required: true - } + }, + subdoc: new AutoInferredSchema({ + name: { type: String, required: true } + }), + docArr: [new AutoInferredSchema({ test: { type: String, required: true } })] + }; + + type UserType = InferRawDocType; + expectType<{ + email: string, + password: string, + dateOfBirth: Date, + subdoc?: { name: string } | null, + docArr: { test: string }[] + }>({} as UserType); +} + +function gh14954() { + const schemaDefinition = { + email: { + type: String, + trim: true, + required: true, + unique: true, + lowercase: true + }, + password: { + type: String, + required: true + }, + dateOfBirth: { + type: Date, + required: true + }, + subdoc: new AutoInferredSchema({ + name: { type: String, required: true }, + l2: new AutoInferredSchema({ + myProp: { type: Number, required: true } + }) + }), + docArr: [new AutoInferredSchema({ test: { type: String, required: true } })] }; - type UserType = InferRawDocType< typeof schemaDefinition>; - expectType<{ email: string, password: string, dateOfBirth: Date }>({} as UserType); + type UserType = InferRawDocType; + expectType<{ + email: string, + password: string, + dateOfBirth: Date, + subdoc?: { name: string, l2?: { myProp: number } | null } | null, + docArr: { test: string }[] + }>({} as UserType); } diff --git a/types/index.d.ts b/types/index.d.ts index b35ead2943..db7aa52d49 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -258,6 +258,27 @@ declare module 'mongoose' { TVirtuals, TStaticMethods> = (schema: Schema, opts?: any) => void; + export class AutoInferredSchema< + SchemaDef = unknown, + RawDocType = InferRawDocType, + TModelType = Model, + TInstanceMethods = {}, + TQueryHelpers = {}, + TVirtuals = {}, + TStaticMethods = {}, + TSchemaOptions = DefaultSchemaOptions, + DocType extends ApplySchemaOptions< + ObtainDocumentType>, + ResolveSchemaOptions + > = ApplySchemaOptions< + ObtainDocumentType>, + ResolveSchemaOptions + >, + THydratedDocumentType = HydratedDocument, TVirtuals & TInstanceMethods> + > extends Schema { + constructor(definition: SchemaDef, options?: SchemaOptions, TInstanceMethods, TQueryHelpers, TStaticMethods, TVirtuals, THydratedDocumentType> | ResolveSchemaOptions); + } + export class Schema< RawDocType = any, TModelType = Model, diff --git a/types/inferrawdoctype.d.ts b/types/inferrawdoctype.d.ts index 9cee6747d8..c284e27b5c 100644 --- a/types/inferrawdoctype.d.ts +++ b/types/inferrawdoctype.d.ts @@ -6,6 +6,7 @@ import { PathWithTypePropertyBaseType, PathEnumOrString } from './inferschematype'; +import { InferSchemaType } from 'mongoose'; declare module 'mongoose' { export type InferRawDocType< @@ -35,6 +36,10 @@ declare module 'mongoose' { TypeKey >; + type InferRawDocTypeFromSchema = TSchema extends AutoInferredSchema + ? InferRawDocType + : InferSchemaType; + /** * Same as inferSchemaType, except: * @@ -49,35 +54,22 @@ declare module 'mongoose' { * @returns Number, "Number" or "number" will be resolved to number type. */ type ResolveRawPathType = {}, TypeKey extends string = DefaultSchemaOptions['typeKey']> = - 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 InferRawDocType to correctly infer its type. - Array> : - IsSchemaTypeFromBuiltinClass extends true ? - ObtainRawDocumentPathType[] : - IsItRecordAndNotAny extends true ? - Item extends Record ? - ObtainRawDocumentPathType[] : - Array> : - ObtainRawDocumentPathType[] - >: - PathValueType extends ReadonlyArray ? + PathValueType extends AutoInferredSchema ? + InferRawDocType : + PathValueType extends Schema ? + InferSchemaType : + PathValueType extends (infer Item)[] ? IfEquals> : + // If Item is a schema, infer its type. + 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[] : - InferRawDocType[]: + // 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[] : IsItRecordAndNotAny extends true ? @@ -86,34 +78,49 @@ declare module 'mongoose' { Array> : 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 ? NativeDate : - IfEquals extends true ? NativeDate : - 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' ? 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 ? InferRawDocType : - unknown; + PathValueType extends ReadonlyArray ? + IfEquals> : + Item extends Record ? + Item[TypeKey] extends Function | String ? + ObtainRawDocumentPathType[] : + InferRawDocType[]: + IsSchemaTypeFromBuiltinClass extends true ? + ObtainRawDocumentPathType[] : + IsItRecordAndNotAny extends true ? + Item extends Record ? + ObtainRawDocumentPathType[] : + Array> : + 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 ? NativeDate : + IfEquals extends true ? NativeDate : + 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' ? 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 ? InferRawDocType : + unknown; }