UNPKG

14.7 kBTypeScriptView Raw
1import {
2 Schema,
3 InferSchemaType,
4 SchemaType,
5 SchemaTypeOptions,
6 TypeKeyBaseType,
7 Types,
8 NumberSchemaDefinition,
9 StringSchemaDefinition,
10 BooleanSchemaDefinition,
11 DateSchemaDefinition,
12 ObtainDocumentType,
13 DefaultTypeKey,
14 ObjectIdSchemaDefinition,
15 IfEquals,
16 DefaultSchemaOptions,
17 IsItRecordAndNotAny
18} from 'mongoose';
19
20declare module 'mongoose' {
21 /**
22 * @summary Obtains document schema type.
23 * @description Obtains document schema type from document Definition OR returns enforced schema type if it's provided.
24 * @param {DocDefinition} DocDefinition A generic equals to the type of document definition "provided in as first parameter in Schema constructor".
25 * @param {EnforcedDocType} EnforcedDocType A generic type enforced by user "provided before schema constructor".
26 * @param {TypeKey} TypeKey A generic of literal string type."Refers to the property used for path type definition".
27 */
28 type ObtainDocumentType<
29 DocDefinition,
30 EnforcedDocType = any,
31 TSchemaOptions extends Record<any, any> = DefaultSchemaOptions
32 > = IsItRecordAndNotAny<EnforcedDocType> extends true ?
33 EnforcedDocType :
34 {
35 [
36 K in keyof (RequiredPaths<DocDefinition, TSchemaOptions['typeKey']> &
37 OptionalPaths<DocDefinition, TSchemaOptions['typeKey']>)
38 ]: IsPathRequired<DocDefinition[K], TSchemaOptions['typeKey']> extends true ?
39 ObtainDocumentPathType<DocDefinition[K], TSchemaOptions['typeKey']> :
40 ObtainDocumentPathType<DocDefinition[K], TSchemaOptions['typeKey']> | null;
41 };
42
43 /**
44 * @summary Obtains document schema type from Schema instance.
45 * @param {Schema} TSchema `typeof` a schema instance.
46 * @example
47 * const userSchema = new Schema({userName:String});
48 * type UserType = InferSchemaType<typeof userSchema>;
49 * // result
50 * type UserType = {userName?: string}
51 */
52 export type InferSchemaType<TSchema> = IfAny<TSchema, any, ObtainSchemaGeneric<TSchema, 'DocType'>>;
53
54 /**
55 * @summary Obtains schema Generic type by using generic alias.
56 * @param {TSchema} TSchema A generic of schema type instance.
57 * @param {alias} alias Targeted generic alias.
58 */
59 type ObtainSchemaGeneric<TSchema, alias extends 'EnforcedDocType' | 'M' | 'TInstanceMethods' | 'TQueryHelpers' | 'TVirtuals' | 'TStaticMethods' | 'TSchemaOptions' | 'DocType'> =
60 TSchema extends Schema<infer EnforcedDocType, infer M, infer TInstanceMethods, infer TQueryHelpers, infer TVirtuals, infer TStaticMethods, infer TSchemaOptions, infer DocType>
61 ? {
62 EnforcedDocType: EnforcedDocType;
63 M: M;
64 TInstanceMethods: TInstanceMethods;
65 TQueryHelpers: TQueryHelpers;
66 TVirtuals: TVirtuals;
67 TStaticMethods: TStaticMethods;
68 TSchemaOptions: TSchemaOptions;
69 DocType: DocType;
70 }[alias]
71 : unknown;
72
73 type ResolveSchemaOptions<T> = MergeType<DefaultSchemaOptions, T>;
74
75 type ApplySchemaOptions<T, O = DefaultSchemaOptions> = ResolveTimestamps<T, O>;
76
77 type ResolveTimestamps<T, O> = O extends { timestamps: true }
78 // For some reason, TypeScript sets all the document properties to unknown
79 // if we use methods, statics, or virtuals. So avoid inferring timestamps
80 // if any of these are set for now. See gh-12807
81 ? O extends { methods: any } | { statics: any } | { virtuals: any }
82 ? T
83 : { createdAt: NativeDate; updatedAt: NativeDate; } & T
84 : T;
85}
86
87type IsPathDefaultUndefined<PathType> = PathType extends { default: undefined } ?
88 true :
89 PathType extends { default: (...args: any[]) => undefined } ?
90 true :
91 false;
92
93/**
94 * @summary Checks if a document path is required or optional.
95 * @param {P} P Document path.
96 * @param {TypeKey} TypeKey A generic of literal string type."Refers to the property used for path type definition".
97 */
98type IsPathRequired<P, TypeKey extends string = DefaultTypeKey> =
99 P extends { required: true | [true, string | undefined] | { isRequired: true } } | ArrayConstructor | any[]
100 ? true
101 : P extends { required: boolean }
102 ? P extends { required: false }
103 ? false
104 : true
105 : P extends (Record<TypeKey, ArrayConstructor | any[]>)
106 ? IsPathDefaultUndefined<P> extends true
107 ? false
108 : true
109 : P extends (Record<TypeKey, any>)
110 ? P extends { default: any }
111 ? IfEquals<P['default'], undefined, false, true>
112 : false
113 : false;
114
115/**
116 * @summary Path base type defined by using TypeKey
117 * @description It helps to check if a path is defined by TypeKey OR not.
118 * @param {TypeKey} TypeKey A literal string refers to path type property key.
119 */
120type PathWithTypePropertyBaseType<TypeKey extends string = DefaultTypeKey> = { [k in TypeKey]: any };
121
122/**
123 * @summary A Utility to obtain schema's required path keys.
124 * @param {T} T A generic refers to document definition.
125 * @param {TypeKey} TypeKey A generic of literal string type."Refers to the property used for path type definition".
126 * @returns required paths keys of document definition.
127 */
128type RequiredPathKeys<T, TypeKey extends string = DefaultTypeKey> = {
129 [K in keyof T]: IsPathRequired<T[K], TypeKey> extends true ? IfEquals<T[K], any, never, K> : never;
130}[keyof T];
131
132/**
133 * @summary A Utility to obtain schema's required paths.
134 * @param {T} T A generic refers to document definition.
135 * @param {TypeKey} TypeKey A generic of literal string type."Refers to the property used for path type definition".
136 * @returns a record contains required paths with the corresponding type.
137 */
138type RequiredPaths<T, TypeKey extends string = DefaultTypeKey> = {
139 [K in RequiredPathKeys<T, TypeKey>]: T[K];
140};
141
142/**
143 * @summary A Utility to obtain schema's optional path keys.
144 * @param {T} T A generic refers to document definition.
145 * @param {TypeKey} TypeKey A generic of literal string type."Refers to the property used for path type definition".
146 * @returns optional paths keys of document definition.
147 */
148type OptionalPathKeys<T, TypeKey extends string = DefaultTypeKey> = {
149 [K in keyof T]: IsPathRequired<T[K], TypeKey> extends true ? never : K;
150}[keyof T];
151
152/**
153 * @summary A Utility to obtain schema's optional paths.
154 * @param {T} T A generic refers to document definition.
155 * @param {TypeKey} TypeKey A generic of literal string type."Refers to the property used for path type definition".
156 * @returns a record contains optional paths with the corresponding type.
157 */
158type OptionalPaths<T, TypeKey extends string = DefaultTypeKey> = {
159 [K in OptionalPathKeys<T, TypeKey>]?: T[K];
160};
161
162/**
163 * @summary Allows users to optionally choose their own type for a schema field for stronger typing.
164 */
165type TypeHint<T> = T extends { __typehint: infer U } ? U: never;
166
167
168/**
169 * @summary Obtains schema Path type.
170 * @description Obtains Path type by separating path type from other options and calling {@link ResolvePathType}
171 * @param {PathValueType} PathValueType Document definition path type.
172 * @param {TypeKey} TypeKey A generic refers to document definition.
173 */
174type ObtainDocumentPathType<PathValueType, TypeKey extends string = DefaultTypeKey> = ResolvePathType<
175PathValueType extends PathWithTypePropertyBaseType<TypeKey> ? PathValueType[TypeKey] : PathValueType,
176PathValueType extends PathWithTypePropertyBaseType<TypeKey> ? Omit<PathValueType, TypeKey> : {},
177TypeKey,
178TypeHint<PathValueType>
179>;
180
181/**
182 * @param {T} T A generic refers to string path enums.
183 * @returns Path enum values type as literal strings or string.
184 */
185type PathEnumOrString<T extends SchemaTypeOptions<string>['enum']> = T extends ReadonlyArray<infer E> ? E : T extends { values: any } ? PathEnumOrString<T['values']> : string;
186
187type IsSchemaTypeFromBuiltinClass<T> = T extends (typeof String)
188 ? true
189 : T extends (typeof Number)
190 ? true
191 : T extends (typeof Boolean)
192 ? true
193 : T extends (typeof Buffer)
194 ? true
195 : T extends (typeof Schema.Types.ObjectId)
196 ? true
197 : T extends (typeof Schema.Types.UUID)
198 ? true
199 : T extends (typeof Schema.Types.Decimal128)
200 ? true
201 : T extends Types.ObjectId
202 ? true
203 : T extends Types.Decimal128
204 ? true
205 : T extends Buffer
206 ? true
207 : T extends (typeof Schema.Types.Mixed)
208 ? true
209 : IfEquals<T, Schema.Types.ObjectId, true, false>;
210
211/**
212 * @summary Resolve path type by returning the corresponding type.
213 * @param {PathValueType} PathValueType Document definition path type.
214 * @param {Options} Options Document definition path options except path type.
215 * @param {TypeKey} TypeKey A generic of literal string type."Refers to the property used for path type definition".
216 * @returns Number, "Number" or "number" will be resolved to number type.
217 */
218type ResolvePathType<PathValueType, Options extends SchemaTypeOptions<PathValueType> = {}, TypeKey extends string = DefaultSchemaOptions['typeKey'], TypeHint = never> =
219 IfEquals<TypeHint, never,
220 PathValueType extends Schema ? InferSchemaType<PathValueType> :
221 PathValueType extends (infer Item)[] ?
222 IfEquals<Item, never, any[], Item extends Schema ?
223 // If Item is a schema, infer its type.
224 Types.DocumentArray<InferSchemaType<Item>> :
225 Item extends Record<TypeKey, any> ?
226 Item[TypeKey] extends Function | String ?
227 // If Item has a type key that's a string or a callable, it must be a scalar,
228 // so we can directly obtain its path type.
229 ObtainDocumentPathType<Item, TypeKey>[] :
230 // If the type key isn't callable, then this is an array of objects, in which case
231 // we need to call ObtainDocumentType to correctly infer its type.
232 Types.DocumentArray<ObtainDocumentType<Item, any, { typeKey: TypeKey }>> :
233 IsSchemaTypeFromBuiltinClass<Item> extends true ?
234 ObtainDocumentPathType<Item, TypeKey>[] :
235 IsItRecordAndNotAny<Item> extends true ?
236 Item extends Record<string, never> ?
237 ObtainDocumentPathType<Item, TypeKey>[] :
238 Types.DocumentArray<ObtainDocumentType<Item, any, { typeKey: TypeKey }>> :
239 ObtainDocumentPathType<Item, TypeKey>[]
240 >:
241 PathValueType extends ReadonlyArray<infer Item> ?
242 IfEquals<Item, never, any[], Item extends Schema ?
243 Types.DocumentArray<InferSchemaType<Item>> :
244 Item extends Record<TypeKey, any> ?
245 Item[TypeKey] extends Function | String ?
246 ObtainDocumentPathType<Item, TypeKey>[] :
247 ObtainDocumentType<Item, any, { typeKey: TypeKey }>[]:
248 IsSchemaTypeFromBuiltinClass<Item> extends true ?
249 ObtainDocumentPathType<Item, TypeKey>[] :
250 IsItRecordAndNotAny<Item> extends true ?
251 Item extends Record<string, never> ?
252 ObtainDocumentPathType<Item, TypeKey>[] :
253 Types.DocumentArray<ObtainDocumentType<Item, any, { typeKey: TypeKey }>> :
254 ObtainDocumentPathType<Item, TypeKey>[]
255 >:
256 PathValueType extends StringSchemaDefinition ? PathEnumOrString<Options['enum']> :
257 IfEquals<PathValueType, Schema.Types.String> extends true ? PathEnumOrString<Options['enum']> :
258 IfEquals<PathValueType, String> extends true ? PathEnumOrString<Options['enum']> :
259 PathValueType extends NumberSchemaDefinition ? Options['enum'] extends ReadonlyArray<any> ? Options['enum'][number] : number :
260 IfEquals<PathValueType, Schema.Types.Number> extends true ? number :
261 PathValueType extends DateSchemaDefinition ? Date :
262 IfEquals<PathValueType, Schema.Types.Date> extends true ? Date :
263 PathValueType extends typeof Buffer | 'buffer' | 'Buffer' | typeof Schema.Types.Buffer ? Buffer :
264 PathValueType extends BooleanSchemaDefinition ? boolean :
265 IfEquals<PathValueType, Schema.Types.Boolean> extends true ? boolean :
266 PathValueType extends ObjectIdSchemaDefinition ? Types.ObjectId :
267 IfEquals<PathValueType, Types.ObjectId> extends true ? Types.ObjectId :
268 IfEquals<PathValueType, Schema.Types.ObjectId> extends true ? Types.ObjectId :
269 PathValueType extends 'decimal128' | 'Decimal128' | typeof Schema.Types.Decimal128 ? Types.Decimal128 :
270 IfEquals<PathValueType, Schema.Types.Decimal128> extends true ? Types.Decimal128 :
271 IfEquals<PathValueType, Types.Decimal128> extends true ? Types.Decimal128 :
272 IfEquals<PathValueType, Schema.Types.BigInt> extends true ? bigint :
273 IfEquals<PathValueType, BigInt> extends true ? bigint :
274 PathValueType extends 'bigint' | 'BigInt' | typeof Schema.Types.BigInt | typeof BigInt ? bigint :
275 PathValueType extends 'uuid' | 'UUID' | typeof Schema.Types.UUID ? Buffer :
276 IfEquals<PathValueType, Schema.Types.UUID> extends true ? Buffer :
277 PathValueType extends MapConstructor | 'Map' ? Map<string, ResolvePathType<Options['of']>> :
278 IfEquals<PathValueType, typeof Schema.Types.Map> extends true ? Map<string, ResolvePathType<Options['of']>> :
279 PathValueType extends ArrayConstructor ? any[] :
280 PathValueType extends typeof Schema.Types.Mixed ? any:
281 IfEquals<PathValueType, ObjectConstructor> extends true ? any:
282 IfEquals<PathValueType, {}> extends true ? any:
283 PathValueType extends typeof SchemaType ? PathValueType['prototype'] :
284 PathValueType extends Record<string, any> ? ObtainDocumentType<PathValueType, any, { typeKey: TypeKey }> :
285 unknown,
286 TypeHint>;