UNPKG

15.8 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 { methods: any } | { statics: any } | { virtuals: any } | { timestamps?: false } ? T
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 { timestamps: infer TimestampOptions } ? TimestampOptions extends true
82 ? { createdAt: NativeDate; updatedAt: NativeDate; } & T
83 : TimestampOptions extends SchemaTimestampsConfig
84 ? {
85 -readonly [K in keyof Pick<
86 TimestampOptions,
87 'createdAt' | 'updatedAt'
88 > as TimestampOptions[K] extends true
89 ? K
90 : TimestampOptions[K] extends string
91 ? TimestampOptions[K]
92 : never]: NativeDate;
93 } & T
94 : T
95 : T;
96}
97
98type IsPathDefaultUndefined<PathType> = PathType extends { default: undefined } ?
99 true :
100 PathType extends { default: (...args: any[]) => undefined } ?
101 true :
102 false;
103
104/**
105 * @summary Checks if a document path is required or optional.
106 * @param {P} P Document path.
107 * @param {TypeKey} TypeKey A generic of literal string type."Refers to the property used for path type definition".
108 */
109type IsPathRequired<P, TypeKey extends string = DefaultTypeKey> =
110 P extends { required: true | [true, string | undefined] | { isRequired: true } } | ArrayConstructor | any[]
111 ? true
112 : P extends { required: boolean }
113 ? P extends { required: false }
114 ? false
115 : true
116 : P extends (Record<TypeKey, ArrayConstructor | any[]>)
117 ? IsPathDefaultUndefined<P> extends true
118 ? false
119 : true
120 : P extends (Record<TypeKey, any>)
121 ? P extends { default: any }
122 ? IfEquals<P['default'], undefined, false, true>
123 : false
124 : false;
125
126/**
127 * @summary Path base type defined by using TypeKey
128 * @description It helps to check if a path is defined by TypeKey OR not.
129 * @param {TypeKey} TypeKey A literal string refers to path type property key.
130 */
131type PathWithTypePropertyBaseType<TypeKey extends string = DefaultTypeKey> = { [k in TypeKey]: any };
132
133/**
134 * @summary A Utility to obtain schema's required path keys.
135 * @param {T} T A generic refers to document definition.
136 * @param {TypeKey} TypeKey A generic of literal string type."Refers to the property used for path type definition".
137 * @returns required paths keys of document definition.
138 */
139type RequiredPathKeys<T, TypeKey extends string = DefaultTypeKey> = {
140 [K in keyof T]: IsPathRequired<T[K], TypeKey> extends true ? IfEquals<T[K], any, never, K> : never;
141}[keyof T];
142
143/**
144 * @summary A Utility to obtain schema's required paths.
145 * @param {T} T A generic refers to document definition.
146 * @param {TypeKey} TypeKey A generic of literal string type."Refers to the property used for path type definition".
147 * @returns a record contains required paths with the corresponding type.
148 */
149type RequiredPaths<T, TypeKey extends string = DefaultTypeKey> = {
150 [K in RequiredPathKeys<T, TypeKey>]: T[K];
151};
152
153/**
154 * @summary A Utility to obtain schema's optional path keys.
155 * @param {T} T A generic refers to document definition.
156 * @param {TypeKey} TypeKey A generic of literal string type."Refers to the property used for path type definition".
157 * @returns optional paths keys of document definition.
158 */
159type OptionalPathKeys<T, TypeKey extends string = DefaultTypeKey> = {
160 [K in keyof T]: IsPathRequired<T[K], TypeKey> extends true ? never : K;
161}[keyof T];
162
163/**
164 * @summary A Utility to obtain schema's optional paths.
165 * @param {T} T A generic refers to document definition.
166 * @param {TypeKey} TypeKey A generic of literal string type."Refers to the property used for path type definition".
167 * @returns a record contains optional paths with the corresponding type.
168 */
169type OptionalPaths<T, TypeKey extends string = DefaultTypeKey> = {
170 [K in OptionalPathKeys<T, TypeKey>]?: T[K];
171};
172
173/**
174 * @summary Allows users to optionally choose their own type for a schema field for stronger typing.
175 */
176type TypeHint<T> = T extends { __typehint: infer U } ? U: never;
177
178
179/**
180 * @summary Obtains schema Path type.
181 * @description Obtains Path type by separating path type from other options and calling {@link ResolvePathType}
182 * @param {PathValueType} PathValueType Document definition path type.
183 * @param {TypeKey} TypeKey A generic refers to document definition.
184 */
185type ObtainDocumentPathType<PathValueType, TypeKey extends string = DefaultTypeKey> = ResolvePathType<
186PathValueType extends PathWithTypePropertyBaseType<TypeKey> ? PathValueType[TypeKey] : PathValueType,
187PathValueType extends PathWithTypePropertyBaseType<TypeKey> ? Omit<PathValueType, TypeKey> : {},
188TypeKey,
189TypeHint<PathValueType>
190>;
191
192/**
193 * @param {T} T A generic refers to string path enums.
194 * @returns Path enum values type as literal strings or string.
195 */
196type PathEnumOrString<T extends SchemaTypeOptions<string>['enum']> = T extends ReadonlyArray<infer E> ? E : T extends { values: any } ? PathEnumOrString<T['values']> : string;
197
198type IsSchemaTypeFromBuiltinClass<T> = T extends (typeof String)
199 ? true
200 : T extends (typeof Number)
201 ? true
202 : T extends (typeof Boolean)
203 ? true
204 : T extends (typeof Buffer)
205 ? true
206 : T extends (typeof Schema.Types.ObjectId)
207 ? true
208 : T extends (typeof Schema.Types.UUID)
209 ? true
210 : T extends (typeof Schema.Types.Decimal128)
211 ? true
212 : T extends (typeof Schema.Types.String)
213 ? true
214 : T extends (typeof Schema.Types.Number)
215 ? true
216 : T extends (typeof Schema.Types.Date)
217 ? true
218 : T extends (typeof Schema.Types.Boolean)
219 ? true
220 : T extends (typeof Schema.Types.Buffer)
221 ? true
222 : T extends Types.ObjectId
223 ? true
224 : T extends Types.Decimal128
225 ? true
226 : T extends Buffer
227 ? true
228 : T extends NativeDate
229 ? true
230 : T extends (typeof Schema.Types.Mixed)
231 ? true
232 : IfEquals<T, Schema.Types.ObjectId, true, false>;
233
234/**
235 * @summary Resolve path type by returning the corresponding type.
236 * @param {PathValueType} PathValueType Document definition path type.
237 * @param {Options} Options Document definition path options except path type.
238 * @param {TypeKey} TypeKey A generic of literal string type."Refers to the property used for path type definition".
239 * @returns Number, "Number" or "number" will be resolved to number type.
240 */
241type ResolvePathType<PathValueType, Options extends SchemaTypeOptions<PathValueType> = {}, TypeKey extends string = DefaultSchemaOptions['typeKey'], TypeHint = never> =
242 IfEquals<TypeHint, never,
243 PathValueType extends Schema ? InferSchemaType<PathValueType> :
244 PathValueType extends (infer Item)[] ?
245 IfEquals<Item, never, any[], Item extends Schema ?
246 // If Item is a schema, infer its type.
247 Types.DocumentArray<InferSchemaType<Item>> :
248 Item extends Record<TypeKey, any> ?
249 Item[TypeKey] extends Function | String ?
250 // If Item has a type key that's a string or a callable, it must be a scalar,
251 // so we can directly obtain its path type.
252 ObtainDocumentPathType<Item, TypeKey>[] :
253 // If the type key isn't callable, then this is an array of objects, in which case
254 // we need to call ObtainDocumentType to correctly infer its type.
255 Types.DocumentArray<ObtainDocumentType<Item, any, { typeKey: TypeKey }>> :
256 IsSchemaTypeFromBuiltinClass<Item> extends true ?
257 ObtainDocumentPathType<Item, TypeKey>[] :
258 IsItRecordAndNotAny<Item> extends true ?
259 Item extends Record<string, never> ?
260 ObtainDocumentPathType<Item, TypeKey>[] :
261 Types.DocumentArray<ObtainDocumentType<Item, any, { typeKey: TypeKey }>> :
262 ObtainDocumentPathType<Item, TypeKey>[]
263 >:
264 PathValueType extends ReadonlyArray<infer Item> ?
265 IfEquals<Item, never, any[], Item extends Schema ?
266 Types.DocumentArray<InferSchemaType<Item>> :
267 Item extends Record<TypeKey, any> ?
268 Item[TypeKey] extends Function | String ?
269 ObtainDocumentPathType<Item, TypeKey>[] :
270 ObtainDocumentType<Item, any, { typeKey: TypeKey }>[]:
271 IsSchemaTypeFromBuiltinClass<Item> extends true ?
272 ObtainDocumentPathType<Item, TypeKey>[] :
273 IsItRecordAndNotAny<Item> extends true ?
274 Item extends Record<string, never> ?
275 ObtainDocumentPathType<Item, TypeKey>[] :
276 Types.DocumentArray<ObtainDocumentType<Item, any, { typeKey: TypeKey }>> :
277 ObtainDocumentPathType<Item, TypeKey>[]
278 >:
279 PathValueType extends StringSchemaDefinition ? PathEnumOrString<Options['enum']> :
280 IfEquals<PathValueType, Schema.Types.String> extends true ? PathEnumOrString<Options['enum']> :
281 IfEquals<PathValueType, String> extends true ? PathEnumOrString<Options['enum']> :
282 PathValueType extends NumberSchemaDefinition ? Options['enum'] extends ReadonlyArray<any> ? Options['enum'][number] : number :
283 IfEquals<PathValueType, Schema.Types.Number> extends true ? number :
284 PathValueType extends DateSchemaDefinition ? Date :
285 IfEquals<PathValueType, Schema.Types.Date> extends true ? Date :
286 PathValueType extends typeof Buffer | 'buffer' | 'Buffer' | typeof Schema.Types.Buffer ? Buffer :
287 PathValueType extends BooleanSchemaDefinition ? boolean :
288 IfEquals<PathValueType, Schema.Types.Boolean> extends true ? boolean :
289 PathValueType extends ObjectIdSchemaDefinition ? Types.ObjectId :
290 IfEquals<PathValueType, Types.ObjectId> extends true ? Types.ObjectId :
291 IfEquals<PathValueType, Schema.Types.ObjectId> extends true ? Types.ObjectId :
292 PathValueType extends 'decimal128' | 'Decimal128' | typeof Schema.Types.Decimal128 ? Types.Decimal128 :
293 IfEquals<PathValueType, Schema.Types.Decimal128> extends true ? Types.Decimal128 :
294 IfEquals<PathValueType, Types.Decimal128> extends true ? Types.Decimal128 :
295 IfEquals<PathValueType, Schema.Types.BigInt> extends true ? bigint :
296 IfEquals<PathValueType, BigInt> extends true ? bigint :
297 PathValueType extends 'bigint' | 'BigInt' | typeof Schema.Types.BigInt | typeof BigInt ? bigint :
298 PathValueType extends 'uuid' | 'UUID' | typeof Schema.Types.UUID ? Buffer :
299 IfEquals<PathValueType, Schema.Types.UUID> extends true ? Buffer :
300 PathValueType extends MapConstructor | 'Map' ? Map<string, ResolvePathType<Options['of']>> :
301 IfEquals<PathValueType, typeof Schema.Types.Map> extends true ? Map<string, ResolvePathType<Options['of']>> :
302 PathValueType extends ArrayConstructor ? any[] :
303 PathValueType extends typeof Schema.Types.Mixed ? any:
304 IfEquals<PathValueType, ObjectConstructor> extends true ? any:
305 IfEquals<PathValueType, {}> extends true ? any:
306 PathValueType extends typeof SchemaType ? PathValueType['prototype'] :
307 PathValueType extends Record<string, any> ? ObtainDocumentType<PathValueType, any, { typeKey: TypeKey }> :
308 unknown,
309 TypeHint>;