Skip to content

Commit b3ef1d8

Browse files
authored
Merge pull request #14008 from JavaScriptBach/add-typehint
Support type hints in InferSchemaType
2 parents eaa1d2d + e7597c6 commit b3ef1d8

File tree

2 files changed

+79
-54
lines changed

2 files changed

+79
-54
lines changed

test/types/schema.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1231,6 +1231,22 @@ async function gh13797() {
12311231
} } });
12321232
}
12331233

1234+
declare const brand: unique symbol;
1235+
function gh14002() {
1236+
type Brand<T, U extends string> = T & { [brand]: U };
1237+
type UserId = Brand<string, 'UserId'>;
1238+
1239+
interface IUser {
1240+
userId: UserId;
1241+
}
1242+
1243+
const userIdTypeHint = 'placeholder' as UserId;
1244+
const schema = new Schema({
1245+
userId: { type: String, required: true, __typehint: userIdTypeHint }
1246+
});
1247+
expectType<IUser>({} as InferSchemaType<typeof schema>);
1248+
}
1249+
12341250
function gh14028_methods() {
12351251
// Methods that have access to `this` should have access to typing of other methods on the schema
12361252
interface IUser {

types/inferschematype.d.ts

Lines changed: 63 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,12 @@ type OptionalPaths<T, TypeKey extends string = DefaultTypeKey> = {
159159
[K in OptionalPathKeys<T, TypeKey>]?: T[K];
160160
};
161161

162+
/**
163+
* @summary Allows users to optionally choose their own type for a schema field for stronger typing.
164+
*/
165+
type TypeHint<T> = T extends { __typehint: infer U } ? U: never;
166+
167+
162168
/**
163169
* @summary Obtains schema Path type.
164170
* @description Obtains Path type by separating path type from other options and calling {@link ResolvePathType}
@@ -168,7 +174,8 @@ type OptionalPaths<T, TypeKey extends string = DefaultTypeKey> = {
168174
type ObtainDocumentPathType<PathValueType, TypeKey extends string = DefaultTypeKey> = ResolvePathType<
169175
PathValueType extends PathWithTypePropertyBaseType<TypeKey> ? PathValueType[TypeKey] : PathValueType,
170176
PathValueType extends PathWithTypePropertyBaseType<TypeKey> ? Omit<PathValueType, TypeKey> : {},
171-
TypeKey
177+
TypeKey,
178+
TypeHint<PathValueType>
172179
>;
173180

174181
/**
@@ -208,35 +215,21 @@ type IsSchemaTypeFromBuiltinClass<T> = T extends (typeof String)
208215
* @param {TypeKey} TypeKey A generic of literal string type."Refers to the property used for path type definition".
209216
* @returns Number, "Number" or "number" will be resolved to number type.
210217
*/
211-
type ResolvePathType<PathValueType, Options extends SchemaTypeOptions<PathValueType> = {}, TypeKey extends string = DefaultSchemaOptions['typeKey']> =
212-
PathValueType extends Schema ? InferSchemaType<PathValueType> :
213-
PathValueType extends (infer Item)[] ?
214-
IfEquals<Item, never, any[], Item extends Schema ?
215-
// If Item is a schema, infer its type.
216-
Types.DocumentArray<InferSchemaType<Item>> :
217-
Item extends Record<TypeKey, any> ?
218-
Item[TypeKey] extends Function | String ?
219-
// If Item has a type key that's a string or a callable, it must be a scalar,
220-
// so we can directly obtain its path type.
221-
ObtainDocumentPathType<Item, TypeKey>[] :
222-
// If the type key isn't callable, then this is an array of objects, in which case
223-
// we need to call ObtainDocumentType to correctly infer its type.
224-
ObtainDocumentType<Item, any, { typeKey: TypeKey }>[] :
225-
IsSchemaTypeFromBuiltinClass<Item> extends true ?
226-
ObtainDocumentPathType<Item, TypeKey>[] :
227-
IsItRecordAndNotAny<Item> extends true ?
228-
Item extends Record<string, never> ?
229-
ObtainDocumentPathType<Item, TypeKey>[] :
230-
Types.DocumentArray<ObtainDocumentType<Item, any, { typeKey: TypeKey }>> :
231-
ObtainDocumentPathType<Item, TypeKey>[]
232-
>:
233-
PathValueType extends ReadonlyArray<infer Item> ?
218+
type 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)[] ?
234222
IfEquals<Item, never, any[], Item extends Schema ?
223+
// If Item is a schema, infer its type.
235224
Types.DocumentArray<InferSchemaType<Item>> :
236225
Item extends Record<TypeKey, any> ?
237226
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.
238229
ObtainDocumentPathType<Item, TypeKey>[] :
239-
ObtainDocumentType<Item, any, { typeKey: 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+
ObtainDocumentType<Item, any, { typeKey: TypeKey }>[] :
240233
IsSchemaTypeFromBuiltinClass<Item> extends true ?
241234
ObtainDocumentPathType<Item, TypeKey>[] :
242235
IsItRecordAndNotAny<Item> extends true ?
@@ -245,32 +238,48 @@ type ResolvePathType<PathValueType, Options extends SchemaTypeOptions<PathValueT
245238
Types.DocumentArray<ObtainDocumentType<Item, any, { typeKey: TypeKey }>> :
246239
ObtainDocumentPathType<Item, TypeKey>[]
247240
>:
248-
PathValueType extends StringSchemaDefinition ? PathEnumOrString<Options['enum']> :
249-
IfEquals<PathValueType, Schema.Types.String> extends true ? PathEnumOrString<Options['enum']> :
250-
IfEquals<PathValueType, String> extends true ? PathEnumOrString<Options['enum']> :
251-
PathValueType extends NumberSchemaDefinition ? Options['enum'] extends ReadonlyArray<any> ? Options['enum'][number] : number :
252-
IfEquals<PathValueType, Schema.Types.Number> extends true ? number :
253-
PathValueType extends DateSchemaDefinition ? Date :
254-
IfEquals<PathValueType, Schema.Types.Date> extends true ? Date :
255-
PathValueType extends typeof Buffer | 'buffer' | 'Buffer' | typeof Schema.Types.Buffer ? Buffer :
256-
PathValueType extends BooleanSchemaDefinition ? boolean :
257-
IfEquals<PathValueType, Schema.Types.Boolean> extends true ? boolean :
258-
PathValueType extends ObjectIdSchemaDefinition ? Types.ObjectId :
259-
IfEquals<PathValueType, Types.ObjectId> extends true ? Types.ObjectId :
260-
IfEquals<PathValueType, Schema.Types.ObjectId> extends true ? Types.ObjectId :
261-
PathValueType extends 'decimal128' | 'Decimal128' | typeof Schema.Types.Decimal128 ? Types.Decimal128 :
262-
IfEquals<PathValueType, Schema.Types.Decimal128> extends true ? Types.Decimal128 :
263-
IfEquals<PathValueType, Types.Decimal128> extends true ? Types.Decimal128 :
264-
IfEquals<PathValueType, Schema.Types.BigInt> extends true ? bigint :
265-
PathValueType extends 'bigint' | 'BigInt' | typeof Schema.Types.BigInt ? bigint :
266-
PathValueType extends 'uuid' | 'UUID' | typeof Schema.Types.UUID ? Buffer :
267-
IfEquals<PathValueType, Schema.Types.UUID> extends true ? Buffer :
268-
PathValueType extends MapConstructor | 'Map' ? Map<string, ResolvePathType<Options['of']>> :
269-
IfEquals<PathValueType, typeof Schema.Types.Map> extends true ? Map<string, ResolvePathType<Options['of']>> :
270-
PathValueType extends ArrayConstructor ? any[] :
271-
PathValueType extends typeof Schema.Types.Mixed ? any:
272-
IfEquals<PathValueType, ObjectConstructor> extends true ? any:
273-
IfEquals<PathValueType, {}> extends true ? any:
274-
PathValueType extends typeof SchemaType ? PathValueType['prototype'] :
275-
PathValueType extends Record<string, any> ? ObtainDocumentType<PathValueType, any, { typeKey: TypeKey }> :
276-
unknown;
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+
PathValueType extends 'bigint' | 'BigInt' | typeof Schema.Types.BigInt ? bigint :
274+
PathValueType extends 'uuid' | 'UUID' | typeof Schema.Types.UUID ? Buffer :
275+
IfEquals<PathValueType, Schema.Types.UUID> extends true ? Buffer :
276+
PathValueType extends MapConstructor | 'Map' ? Map<string, ResolvePathType<Options['of']>> :
277+
IfEquals<PathValueType, typeof Schema.Types.Map> extends true ? Map<string, ResolvePathType<Options['of']>> :
278+
PathValueType extends ArrayConstructor ? any[] :
279+
PathValueType extends typeof Schema.Types.Mixed ? any:
280+
IfEquals<PathValueType, ObjectConstructor> extends true ? any:
281+
IfEquals<PathValueType, {}> extends true ? any:
282+
PathValueType extends typeof SchemaType ? PathValueType['prototype'] :
283+
PathValueType extends Record<string, any> ? ObtainDocumentType<PathValueType, any, { typeKey: TypeKey }> :
284+
unknown,
285+
TypeHint>;

0 commit comments

Comments
 (0)