mongoose
Version:
Mongoose MongoDB ODM
327 lines (307 loc) • 16.6 kB
TypeScript
import {
Schema,
InferSchemaType,
SchemaType,
SchemaTypeOptions,
TypeKeyBaseType,
Types,
NumberSchemaDefinition,
StringSchemaDefinition,
BooleanSchemaDefinition,
DateSchemaDefinition,
ObtainDocumentType,
DefaultTypeKey,
ObjectIdSchemaDefinition,
IfEquals,
DefaultSchemaOptions,
IsItRecordAndNotAny
} from 'mongoose';
declare module 'mongoose' {
/**
* @summary Obtains document schema type.
* @description Obtains document schema type from document Definition OR returns enforced schema type if it's provided.
* @param {DocDefinition} DocDefinition A generic equals to the type of document definition "provided in as first parameter in Schema constructor".
* @param {EnforcedDocType} EnforcedDocType A generic type enforced by user "provided before schema constructor".
* @param {TypeKey} TypeKey A generic of literal string type."Refers to the property used for path type definition".
*/
type ObtainDocumentType<
DocDefinition,
EnforcedDocType = any,
TSchemaOptions extends Record<any, any> = DefaultSchemaOptions
> = IsItRecordAndNotAny<EnforcedDocType> extends true ?
EnforcedDocType :
{
[
K in keyof (RequiredPaths<DocDefinition, TSchemaOptions['typeKey']> &
OptionalPaths<DocDefinition, TSchemaOptions['typeKey']>)
]: IsPathRequired<DocDefinition[K], TSchemaOptions['typeKey']> extends true ?
ObtainDocumentPathType<DocDefinition[K], TSchemaOptions['typeKey']> :
ObtainDocumentPathType<DocDefinition[K], TSchemaOptions['typeKey']> | null;
};
/**
* @summary Obtains document schema type from Schema instance.
* @param {Schema} TSchema `typeof` a schema instance.
* @example
* const userSchema = new Schema({userName:String});
* type UserType = InferSchemaType<typeof userSchema>;
* // result
* type UserType = {userName?: string}
*/
export type InferSchemaType<TSchema> = IfAny<TSchema, any, ObtainSchemaGeneric<TSchema, 'DocType'>>;
/**
* @summary Obtains schema Generic type by using generic alias.
* @param {TSchema} TSchema A generic of schema type instance.
* @param {alias} alias Targeted generic alias.
*/
type ObtainSchemaGeneric<TSchema, alias extends 'EnforcedDocType' | 'M' | 'TInstanceMethods' | 'TQueryHelpers' | 'TVirtuals' | 'TStaticMethods' | 'TSchemaOptions' | 'DocType'> =
TSchema extends Schema<infer EnforcedDocType, infer M, infer TInstanceMethods, infer TQueryHelpers, infer TVirtuals, infer TStaticMethods, infer TSchemaOptions, infer DocType>
? {
EnforcedDocType: EnforcedDocType;
M: M;
TInstanceMethods: TInstanceMethods;
TQueryHelpers: TQueryHelpers;
TVirtuals: TVirtuals;
TStaticMethods: TStaticMethods;
TSchemaOptions: TSchemaOptions;
DocType: DocType;
}[alias]
: unknown;
type ResolveSchemaOptions<T> = MergeType<DefaultSchemaOptions, T>;
type ApplySchemaOptions<T, O = DefaultSchemaOptions> = ResolveTimestamps<T, O>;
type ResolveTimestamps<T, O> = O extends { methods: any } | { statics: any } | { virtuals: any } | { timestamps?: false } ? T
// For some reason, TypeScript sets all the document properties to unknown
// if we use methods, statics, or virtuals. So avoid inferring timestamps
// if any of these are set for now. See gh-12807
: O extends { timestamps: infer TimestampOptions } ? TimestampOptions extends true
? { createdAt: NativeDate; updatedAt: NativeDate; } & T
: TimestampOptions extends SchemaTimestampsConfig
? {
-readonly [K in keyof Pick<
TimestampOptions,
'createdAt' | 'updatedAt'
> as TimestampOptions[K] extends true
? K
: TimestampOptions[K] extends `${infer TimestampValue}`
? TimestampValue
: never]: NativeDate;
} & T
: T
: T;
}
type IsPathDefaultUndefined<PathType> = 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, TypeKey extends string = DefaultTypeKey> =
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<TypeKey, ArrayConstructor | any[]>)
? IsPathDefaultUndefined<P> extends true
? false
: true
: P extends (Record<TypeKey, any>)
? P extends { default: any }
? IfEquals<P['default'], undefined, false, true>
: 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<TypeKey extends string = DefaultTypeKey> = { [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<T, TypeKey extends string = DefaultTypeKey> = {
[K in keyof T]: IsPathRequired<T[K], TypeKey> extends true ? IfEquals<T[K], any, never, K> : 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<T, TypeKey extends string = DefaultTypeKey> = Pick<
{ -readonly [K in keyof T]: T[K] },
RequiredPathKeys<T, 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<T, TypeKey extends string = DefaultTypeKey> = {
[K in keyof T]: IsPathRequired<T[K], TypeKey> extends true ? never : K;
}[keyof T];
/**
* @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<T, TypeKey extends string = DefaultTypeKey> = Pick<
{ -readonly [K in keyof T]?: T[K] },
OptionalPathKeys<T, TypeKey>
>;
/**
* @summary Allows users to optionally choose their own type for a schema field for stronger typing.
*/
type TypeHint<T> = T extends { __typehint: infer U } ? U: never;
/**
* @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 ObtainDocumentPathType<PathValueType, TypeKey extends string = DefaultTypeKey> = ResolvePathType<
PathValueType extends PathWithTypePropertyBaseType<TypeKey>
? PathValueType[TypeKey] extends PathWithTypePropertyBaseType<TypeKey>
? PathValueType
: PathValueType[TypeKey]
: PathValueType,
PathValueType extends PathWithTypePropertyBaseType<TypeKey>
? PathValueType[TypeKey] extends PathWithTypePropertyBaseType<TypeKey>
? {}
: Omit<PathValueType, TypeKey>
: {},
TypeKey,
TypeHint<PathValueType>
>;
/**
* @param {T} T A generic refers to string path enums.
* @returns Path enum values type as literal strings or string.
*/
type PathEnumOrString<T extends SchemaTypeOptions<string>['enum']> = T extends ReadonlyArray<infer E> ? E : T extends { values: any } ? PathEnumOrString<T['values']> : string;
type IsSchemaTypeFromBuiltinClass<T> = T extends (typeof String)
? true
: T extends (typeof Number)
? true
: T extends (typeof Boolean)
? true
: T extends (typeof Buffer)
? true
: T extends (typeof Schema.Types.ObjectId)
? true
: T extends (typeof Schema.Types.UUID)
? true
: T extends (typeof Schema.Types.Decimal128)
? true
: T extends (typeof Schema.Types.Int32)
? true
: T extends (typeof Schema.Types.String)
? true
: T extends (typeof Schema.Types.Number)
? true
: T extends (typeof Schema.Types.Date)
? true
: T extends (typeof Schema.Types.Double)
? true
: T extends (typeof Schema.Types.Boolean)
? true
: T extends Types.ObjectId
? true
: T extends Types.Decimal128
? true
: T extends NativeDate
? true
: T extends (typeof Schema.Types.Mixed)
? true
: IfEquals<T, Schema.Types.ObjectId, true, false> extends true
? true
: unknown extends Buffer
? false
: T extends Buffer
? true
: false;
/**
* @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 ResolvePathType<PathValueType, Options extends SchemaTypeOptions<PathValueType> = {}, TypeKey extends string = DefaultSchemaOptions['typeKey'], TypeHint = never> =
IfEquals<TypeHint, never,
PathValueType extends Schema ? InferSchemaType<PathValueType> :
PathValueType extends (infer Item)[] ?
IfEquals<Item, never, any[], Item extends Schema ?
// If Item is a schema, infer its type.
Types.DocumentArray<InferSchemaType<Item>> :
Item extends Record<TypeKey, any> ?
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.
ObtainDocumentPathType<Item, TypeKey>[] :
// 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.
Types.DocumentArray<ObtainDocumentType<Item, any, { typeKey: TypeKey }>> :
IsSchemaTypeFromBuiltinClass<Item> extends true ?
ObtainDocumentPathType<Item, TypeKey>[] :
IsItRecordAndNotAny<Item> extends true ?
Item extends Record<string, never> ?
ObtainDocumentPathType<Item, TypeKey>[] :
Types.DocumentArray<ObtainDocumentType<Item, any, { typeKey: TypeKey }>> :
ObtainDocumentPathType<Item, TypeKey>[]
>:
PathValueType extends ReadonlyArray<infer Item> ?
IfEquals<Item, never, any[], Item extends Schema ?
Types.DocumentArray<InferSchemaType<Item>> :
Item extends Record<TypeKey, any> ?
Item[TypeKey] extends Function | String ?
ObtainDocumentPathType<Item, TypeKey>[] :
ObtainDocumentType<Item, any, { typeKey: TypeKey }>[]:
IsSchemaTypeFromBuiltinClass<Item> extends true ?
ObtainDocumentPathType<Item, TypeKey>[] :
IsItRecordAndNotAny<Item> extends true ?
Item extends Record<string, never> ?
ObtainDocumentPathType<Item, TypeKey>[] :
Types.DocumentArray<ObtainDocumentType<Item, any, { typeKey: TypeKey }>> :
ObtainDocumentPathType<Item, TypeKey>[]
>:
PathValueType extends StringSchemaDefinition ? PathEnumOrString<Options['enum']> :
IfEquals<PathValueType, Schema.Types.String> extends true ? PathEnumOrString<Options['enum']> :
IfEquals<PathValueType, String> extends true ? PathEnumOrString<Options['enum']> :
PathValueType extends NumberSchemaDefinition ? Options['enum'] extends ReadonlyArray<any> ? Options['enum'][number] : number :
IfEquals<PathValueType, Schema.Types.Number> extends true ? number :
PathValueType extends DateSchemaDefinition ? NativeDate :
IfEquals<PathValueType, Schema.Types.Date> extends true ? NativeDate :
PathValueType extends typeof Buffer | 'buffer' | 'Buffer' | typeof Schema.Types.Buffer ? Buffer :
PathValueType extends BooleanSchemaDefinition ? boolean :
IfEquals<PathValueType, Schema.Types.Boolean> extends true ? boolean :
PathValueType extends ObjectIdSchemaDefinition ? Types.ObjectId :
IfEquals<PathValueType, Types.ObjectId> extends true ? Types.ObjectId :
IfEquals<PathValueType, Schema.Types.ObjectId> extends true ? Types.ObjectId :
PathValueType extends 'decimal128' | 'Decimal128' | typeof Schema.Types.Decimal128 ? Types.Decimal128 :
IfEquals<PathValueType, Schema.Types.Decimal128> extends true ? Types.Decimal128 :
IfEquals<PathValueType, Types.Decimal128> extends true ? Types.Decimal128 :
IfEquals<PathValueType, Schema.Types.BigInt> extends true ? bigint :
IfEquals<PathValueType, BigInt> extends true ? bigint :
PathValueType extends 'bigint' | 'BigInt' | typeof Schema.Types.BigInt | typeof BigInt ? bigint :
PathValueType extends 'uuid' | 'UUID' | typeof Schema.Types.UUID ? Buffer :
PathValueType extends 'double' | 'Double' | typeof Schema.Types.Double ? Types.Double :
IfEquals<PathValueType, Schema.Types.UUID> extends true ? Buffer :
PathValueType extends MapConstructor | 'Map' ? Map<string, ResolvePathType<Options['of']>> :
IfEquals<PathValueType, typeof Schema.Types.Map> extends true ? Map<string, ResolvePathType<Options['of']>> :
PathValueType extends ArrayConstructor ? any[] :
PathValueType extends typeof Schema.Types.Mixed ? any:
IfEquals<PathValueType, ObjectConstructor> extends true ? any:
IfEquals<PathValueType, {}> extends true ? any:
PathValueType extends typeof SchemaType ? PathValueType['prototype'] :
PathValueType extends Record<string, any> ? ObtainDocumentType<PathValueType, any, { typeKey: TypeKey }> :
unknown,
TypeHint>;