diff --git a/lib/schema.js b/lib/schema.js index c07d40bef1c..a62f16287df 100644 --- a/lib/schema.js +++ b/lib/schema.js @@ -1110,6 +1110,7 @@ Schema.prototype.interpretAsType = function(path, obj, options) { // If this schema has an associated Mongoose object, use the Mongoose object's // copy of SchemaTypes re: gh-7158 gh-6933 const MongooseTypes = this.base != null ? this.base.Schema.Types : Schema.Types; + const Types = this.base != null ? this.base.Types : require('./types'); if (!utils.isPOJO(obj) && !(obj instanceof SchemaTypeOptions)) { const constructorName = utils.getFunctionName(obj.constructor); @@ -1244,6 +1245,10 @@ Schema.prototype.interpretAsType = function(path, obj, options) { name = 'Buffer'; } else if (typeof type === 'function' || typeof type === 'object') { name = type.schemaName || utils.getFunctionName(type); + } else if (type === Types.ObjectId) { + name = 'ObjectId'; + } else if (type === Types.Decimal128) { + name = 'Decimal128'; } else { name = type == null ? '' + type : type.toString(); } diff --git a/test/schema.test.js b/test/schema.test.js index c09895de3b3..36368c58b2d 100644 --- a/test/schema.test.js +++ b/test/schema.test.js @@ -2867,4 +2867,22 @@ describe('schema', function() { assert.equal(doc1.domain, mongooseDomain); assert.equal(doc1.domain, doc2.domain); }); + + it('allows defining ObjectIds and Decimal128s using Types.* (gh-12205)', function() { + const schema = new Schema({ + testId: mongoose.Types.ObjectId, + testId2: { + type: mongoose.Types.ObjectId + }, + num: mongoose.Types.Decimal128, + num2: { + type: mongoose.Types.Decimal128 + } + }); + + assert.equal(schema.path('testId').instance, 'ObjectID'); + assert.equal(schema.path('testId2').instance, 'ObjectID'); + assert.equal(schema.path('num').instance, 'Decimal128'); + assert.equal(schema.path('num2').instance, 'Decimal128'); + }); }); diff --git a/test/types/schema.test.ts b/test/types/schema.test.ts index 66ed8ee28d9..4a6275d7c94 100644 --- a/test/types/schema.test.ts +++ b/test/types/schema.test.ts @@ -8,10 +8,14 @@ import { InferSchemaType, SchemaType, Query, + model, HydratedDocument, - SchemaOptions + SchemaOptions, + ObtainDocumentType, + ObtainSchemaGeneric } from 'mongoose'; import { expectType, expectError, expectAssignable } from 'tsd'; +import { ObtainDocumentPathType, ResolvePathType } from '../../types/inferschematype'; enum Genre { Action, @@ -665,6 +669,28 @@ function gh12030() { ] }); + type A = ResolvePathType<[ + { + username: { type: String } + } + ]>; + expectType<{ + username?: string + }[]>({} as A); + + type B = ObtainDocumentType<{ + users: [ + { + username: { type: String } + } + ] + }>; + expectType<{ + users: { + username?: string + }[]; + }>({} as B); + expectType<{ users: { username?: string @@ -763,6 +789,71 @@ function pluginOptions() { expectError(schema.plugin(pluginFunction2, {})); // should error because "option2" is not optional } +function gh12205() { + const campaignSchema = new Schema( + { + client: { + type: new Types.ObjectId(), + required: true + } + }, + { timestamps: true } + ); + + const Campaign = model('Campaign', campaignSchema); + const doc = new Campaign(); + expectType(doc.client); + + type ICampaign = InferSchemaType; + expectType<{ client: Types.ObjectId }>({} as ICampaign); + + expectType<'type'>({} as ObtainSchemaGeneric); + + type A = ObtainDocumentType<{ client: { type: Schema.Types.ObjectId, required: true } }>; + expectType<{ client: Types.ObjectId }>({} as A); + + type Foo = ObtainDocumentPathType<{ type: Schema.Types.ObjectId, required: true }, 'type'>; + expectType({} as Foo); + + type Bar = ResolvePathType; + expectType({} as Bar); + + /* type Baz = Schema.Types.ObjectId extends typeof Schema.Types.ObjectId ? string : number; + expectType({} as Baz); */ +} + + +function gh12450() { + const ObjectIdSchema = new Schema({ + user: { type: Schema.Types.ObjectId } + }); + + expectType<{ + user?: Types.ObjectId; + }>({} as InferSchemaType); + + const Schema2 = new Schema({ + createdAt: { type: Date, required: true }, + decimalValue: { type: Schema.Types.Decimal128, required: true } + }); + + expectType<{ createdAt: Date, decimalValue: Types.Decimal128 }>({} as InferSchemaType); + + const Schema3 = new Schema({ + createdAt: { type: Date, required: true }, + decimalValue: { type: Schema.Types.Decimal128 } + }); + + expectType<{ createdAt: Date, decimalValue?: Types.Decimal128 }>({} as InferSchemaType); + + const Schema4 = new Schema({ + createdAt: { type: Date }, + decimalValue: { type: Schema.Types.Decimal128 } + }); + + expectType<{ createdAt?: Date, decimalValue?: Types.Decimal128 }>({} as InferSchemaType); +} + function gh12242() { const dbExample = new Schema( { diff --git a/types/inferschematype.d.ts b/types/inferschematype.d.ts index d590b5d65c9..a0406388f00 100644 --- a/types/inferschematype.d.ts +++ b/types/inferschematype.d.ts @@ -31,14 +31,14 @@ declare module 'mongoose' { /** * @summary Obtains document schema type from Schema instance. - * @param {SchemaType} SchemaType A generic of schema type instance. + * @param {Schema} TSchema `typeof` a schema instance. * @example * const userSchema = new Schema({userName:String}); * type UserType = InferSchemaType; * // result * type UserType = {userName?: string} */ - type InferSchemaType = ObtainSchemaGeneric; + type InferSchemaType = ObtainSchemaGeneric; /** * @summary Obtains schema Generic type by using generic alias. @@ -65,7 +65,7 @@ declare module 'mongoose' { * @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 = +type IsPathRequired = P extends { required: true | [true, string | undefined] } | ArrayConstructor | any[] ? true : P extends (Record) @@ -83,7 +83,7 @@ type IsPathRequired = * @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 }; +type PathWithTypePropertyBaseType = { [k in TypeKey]: any }; /** * @summary A Utility to obtain schema's required path keys. @@ -91,7 +91,7 @@ type PathWithTypePropertyBaseType = { [k in Typ * @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 = { +type RequiredPathKeys = { [K in keyof T]: IsPathRequired extends true ? IfEquals : never; }[keyof T]; @@ -101,7 +101,7 @@ type RequiredPathKeys = { * @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 = { +type RequiredPaths = { [K in RequiredPathKeys]: T[K]; }; @@ -111,7 +111,7 @@ type RequiredPaths = { * @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 = { +type OptionalPathKeys = { [K in keyof T]: IsPathRequired extends true ? never : K; }[keyof T]; @@ -121,7 +121,7 @@ type OptionalPathKeys = { * @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 = { +type OptionalPaths = { [K in OptionalPathKeys]?: T[K]; }; @@ -131,7 +131,7 @@ type OptionalPaths = { * @param {PathValueType} PathValueType Document definition path type. * @param {TypeKey} TypeKey A generic refers to document definition. */ -type ObtainDocumentPathType = PathValueType extends Schema +type ObtainDocumentPathType = PathValueType extends Schema ? InferSchemaType : ResolvePathType< PathValueType extends PathWithTypePropertyBaseType ? PathValueType[TypeKey] : PathValueType, @@ -154,19 +154,28 @@ type PathEnumOrString['enum']> = T extends ( */ type ResolvePathType = {}, TypeKey extends TypeKeyBaseType = DefaultTypeKey> = PathValueType extends Schema ? InferSchemaType : - PathValueType extends (infer Item)[] ? IfEquals> : ResolvePathType[]> : + PathValueType extends (infer Item)[] ? IfEquals> : ObtainDocumentPathType[]> : PathValueType extends StringSchemaDefinition ? PathEnumOrString : - PathValueType extends NumberSchemaDefinition ? Options['enum'] extends ReadonlyArray ? Options['enum'][number] : number : - PathValueType extends DateSchemaDefinition ? Date : - PathValueType extends typeof Buffer | 'buffer' | 'Buffer' | typeof Schema.Types.Buffer ? Buffer : - PathValueType extends BooleanSchemaDefinition ? boolean : - PathValueType extends ObjectIdSchemaDefinition ? Types.ObjectId : - PathValueType extends 'decimal128' | 'Decimal128' | typeof Schema.Types.Decimal128 ? Types.Decimal128 : - PathValueType extends MapConstructor ? 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; + 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 : + PathValueType extends MapConstructor ? 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;