Skip to content

Commit 61ad105

Browse files
Support type hints in InferSchemaType
1 parent db92dd9 commit 61ad105

File tree

2 files changed

+80
-55
lines changed

2 files changed

+80
-55
lines changed

test/types/schema.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1230,3 +1230,19 @@ async function gh13797() {
12301230
expectType<IUser>(this); return '';
12311231
} } });
12321232
}
1233+
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+
}

types/inferschematype.d.ts

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

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

173180
/**
@@ -183,57 +190,59 @@ type PathEnumOrString<T extends SchemaTypeOptions<string>['enum']> = T extends R
183190
* @param {TypeKey} TypeKey A generic of literal string type."Refers to the property used for path type definition".
184191
* @returns Number, "Number" or "number" will be resolved to number type.
185192
*/
186-
type ResolvePathType<PathValueType, Options extends SchemaTypeOptions<PathValueType> = {}, TypeKey extends string = DefaultSchemaOptions['typeKey']> =
187-
PathValueType extends Schema ? InferSchemaType<PathValueType> :
188-
PathValueType extends (infer Item)[] ?
189-
IfEquals<Item, never, any[], Item extends Schema ?
190-
// If Item is a schema, infer its type.
191-
Types.DocumentArray<InferSchemaType<Item>> :
192-
Item extends Record<TypeKey, any>?
193-
Item[TypeKey] extends Function | String ?
194-
// If Item has a type key that's a string or a callable, it must be a scalar,
195-
// so we can directly obtain its path type.
196-
ObtainDocumentPathType<Item, TypeKey>[] :
197-
// If the type key isn't callable, then this is an array of objects, in which case
198-
// we need to call ObtainDocumentType to correctly infer its type.
199-
ObtainDocumentType<Item, any, { typeKey: TypeKey }>[]:
200-
ObtainDocumentPathType<Item, TypeKey>[]
201-
>:
202-
PathValueType extends ReadonlyArray<infer Item> ?
203-
IfEquals<Item, never, any[], Item extends Schema ?
204-
Types.DocumentArray<InferSchemaType<Item>> :
205-
Item extends Record<TypeKey, any> ?
206-
Item[TypeKey] extends Function | String ?
207-
ObtainDocumentPathType<Item, TypeKey>[] :
208-
ObtainDocumentType<Item, any, { typeKey: TypeKey }>[]:
209-
ObtainDocumentPathType<Item, TypeKey>[]
210-
>:
211-
PathValueType extends StringSchemaDefinition ? PathEnumOrString<Options['enum']> :
212-
IfEquals<PathValueType, Schema.Types.String> extends true ? PathEnumOrString<Options['enum']> :
213-
IfEquals<PathValueType, String> extends true ? PathEnumOrString<Options['enum']> :
214-
PathValueType extends NumberSchemaDefinition ? Options['enum'] extends ReadonlyArray<any> ? Options['enum'][number] : number :
215-
IfEquals<PathValueType, Schema.Types.Number> extends true ? number :
216-
PathValueType extends DateSchemaDefinition ? Date :
217-
IfEquals<PathValueType, Schema.Types.Date> extends true ? Date :
218-
PathValueType extends typeof Buffer | 'buffer' | 'Buffer' | typeof Schema.Types.Buffer ? Buffer :
219-
PathValueType extends BooleanSchemaDefinition ? boolean :
220-
IfEquals<PathValueType, Schema.Types.Boolean> extends true ? boolean :
221-
PathValueType extends ObjectIdSchemaDefinition ? Types.ObjectId :
222-
IfEquals<PathValueType, Types.ObjectId> extends true ? Types.ObjectId :
223-
IfEquals<PathValueType, Schema.Types.ObjectId> extends true ? Types.ObjectId :
224-
PathValueType extends 'decimal128' | 'Decimal128' | typeof Schema.Types.Decimal128 ? Types.Decimal128 :
225-
IfEquals<PathValueType, Schema.Types.Decimal128> extends true ? Types.Decimal128 :
226-
IfEquals<PathValueType, Types.Decimal128> extends true ? Types.Decimal128 :
227-
IfEquals<PathValueType, Schema.Types.BigInt> extends true ? bigint :
228-
PathValueType extends 'bigint' | 'BigInt' | typeof Schema.Types.BigInt ? bigint :
229-
PathValueType extends 'uuid' | 'UUID' | typeof Schema.Types.UUID ? Buffer :
230-
IfEquals<PathValueType, Schema.Types.UUID> extends true ? Buffer :
231-
PathValueType extends MapConstructor | 'Map' ? Map<string, ResolvePathType<Options['of']>> :
232-
IfEquals<PathValueType, typeof Schema.Types.Map> extends true ? Map<string, ResolvePathType<Options['of']>> :
233-
PathValueType extends ArrayConstructor ? any[] :
234-
PathValueType extends typeof Schema.Types.Mixed ? any:
235-
IfEquals<PathValueType, ObjectConstructor> extends true ? any:
236-
IfEquals<PathValueType, {}> extends true ? any:
237-
PathValueType extends typeof SchemaType ? PathValueType['prototype'] :
238-
PathValueType extends Record<string, any> ? ObtainDocumentType<PathValueType, any, { typeKey: TypeKey }> :
239-
unknown;
193+
type ResolvePathType<PathValueType, Options extends SchemaTypeOptions<PathValueType> = {}, TypeKey extends string = DefaultSchemaOptions['typeKey'], TypeHint = never> =
194+
IfEquals<TypeHint, never,
195+
PathValueType extends Schema ? InferSchemaType<PathValueType> :
196+
PathValueType extends (infer Item)[] ?
197+
IfEquals<Item, never, any[], Item extends Schema ?
198+
// If Item is a schema, infer its type.
199+
Types.DocumentArray<InferSchemaType<Item>> :
200+
Item extends Record<TypeKey, any>?
201+
Item[TypeKey] extends Function | String ?
202+
// If Item has a type key that's a string or a callable, it must be a scalar,
203+
// so we can directly obtain its path type.
204+
ObtainDocumentPathType<Item, TypeKey>[] :
205+
// If the type key isn't callable, then this is an array of objects, in which case
206+
// we need to call ObtainDocumentType to correctly infer its type.
207+
ObtainDocumentType<Item, any, { typeKey: TypeKey }>[]:
208+
ObtainDocumentPathType<Item, TypeKey>[]
209+
>:
210+
PathValueType extends ReadonlyArray<infer Item> ?
211+
IfEquals<Item, never, any[], Item extends Schema ?
212+
Types.DocumentArray<InferSchemaType<Item>> :
213+
Item extends Record<TypeKey, any> ?
214+
Item[TypeKey] extends Function | String ?
215+
ObtainDocumentPathType<Item, TypeKey>[] :
216+
ObtainDocumentType<Item, any, { typeKey: TypeKey }>[]:
217+
ObtainDocumentPathType<Item, TypeKey>[]
218+
>:
219+
PathValueType extends StringSchemaDefinition ? PathEnumOrString<Options['enum']> :
220+
IfEquals<PathValueType, Schema.Types.String> extends true ? PathEnumOrString<Options['enum']> :
221+
IfEquals<PathValueType, String> extends true ? PathEnumOrString<Options['enum']> :
222+
PathValueType extends NumberSchemaDefinition ? Options['enum'] extends ReadonlyArray<any> ? Options['enum'][number] : number :
223+
IfEquals<PathValueType, Schema.Types.Number> extends true ? number :
224+
PathValueType extends DateSchemaDefinition ? Date :
225+
IfEquals<PathValueType, Schema.Types.Date> extends true ? Date :
226+
PathValueType extends typeof Buffer | 'buffer' | 'Buffer' | typeof Schema.Types.Buffer ? Buffer :
227+
PathValueType extends BooleanSchemaDefinition ? boolean :
228+
IfEquals<PathValueType, Schema.Types.Boolean> extends true ? boolean :
229+
PathValueType extends ObjectIdSchemaDefinition ? Types.ObjectId :
230+
IfEquals<PathValueType, Types.ObjectId> extends true ? Types.ObjectId :
231+
IfEquals<PathValueType, Schema.Types.ObjectId> extends true ? Types.ObjectId :
232+
PathValueType extends 'decimal128' | 'Decimal128' | typeof Schema.Types.Decimal128 ? Types.Decimal128 :
233+
IfEquals<PathValueType, Schema.Types.Decimal128> extends true ? Types.Decimal128 :
234+
IfEquals<PathValueType, Types.Decimal128> extends true ? Types.Decimal128 :
235+
IfEquals<PathValueType, Schema.Types.BigInt> extends true ? bigint :
236+
PathValueType extends 'bigint' | 'BigInt' | typeof Schema.Types.BigInt ? bigint :
237+
PathValueType extends 'uuid' | 'UUID' | typeof Schema.Types.UUID ? Buffer :
238+
IfEquals<PathValueType, Schema.Types.UUID> extends true ? Buffer :
239+
PathValueType extends MapConstructor | 'Map' ? Map<string, ResolvePathType<Options['of']>> :
240+
IfEquals<PathValueType, typeof Schema.Types.Map> extends true ? Map<string, ResolvePathType<Options['of']>> :
241+
PathValueType extends ArrayConstructor ? any[] :
242+
PathValueType extends typeof Schema.Types.Mixed ? any:
243+
IfEquals<PathValueType, ObjectConstructor> extends true ? any:
244+
IfEquals<PathValueType, {}> extends true ? any:
245+
PathValueType extends typeof SchemaType ? PathValueType['prototype'] :
246+
PathValueType extends Record<string, any> ? ObtainDocumentType<PathValueType, any, { typeKey: TypeKey }> :
247+
unknown
248+
, TypeHint>;

0 commit comments

Comments
 (0)