Skip to content

Commit ed8eacf

Browse files
authored
Merge pull request #12884 from JavaScriptBach/array-type-fix
Correctly infer types on document arrays
2 parents 256a11e + 054b704 commit ed8eacf

File tree

2 files changed

+131
-10
lines changed

2 files changed

+131
-10
lines changed

test/types/schema.test.ts

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,3 +1018,105 @@ function gh12869() {
10181018
type Example = InferSchemaType<typeof dbExample>;
10191019
expectType<'foo' | 'bar'>({} as Example['active']);
10201020
}
1021+
1022+
function gh12882() {
1023+
// Array of strings
1024+
const arrString = new Schema({
1025+
fooArray: {
1026+
type: [{
1027+
type: String,
1028+
required: true
1029+
}],
1030+
required: true
1031+
}
1032+
});
1033+
type tArrString = InferSchemaType<typeof arrString>;
1034+
// Array of numbers using string definition
1035+
const arrNum = new Schema({
1036+
fooArray: {
1037+
type: [{
1038+
type: 'Number',
1039+
required: true
1040+
}],
1041+
required: true
1042+
}
1043+
});
1044+
type tArrNum = InferSchemaType<typeof arrNum>;
1045+
expectType<{
1046+
fooArray: number[]
1047+
}>({} as tArrNum);
1048+
// Array of object with key named "type"
1049+
const arrType = new Schema({
1050+
fooArray: {
1051+
type: [{
1052+
type: {
1053+
type: String,
1054+
required: true
1055+
},
1056+
foo: {
1057+
type: Number,
1058+
required: true
1059+
}
1060+
}],
1061+
required: true
1062+
}
1063+
});
1064+
type tArrType = InferSchemaType<typeof arrType>;
1065+
expectType<{
1066+
fooArray: {
1067+
type: string;
1068+
foo: number;
1069+
}[]
1070+
}>({} as tArrType);
1071+
// Readonly array of strings
1072+
const rArrString = new Schema({
1073+
fooArray: {
1074+
type: [{
1075+
type: String,
1076+
required: true
1077+
}] as const,
1078+
required: true
1079+
}
1080+
});
1081+
type rTArrString = InferSchemaType<typeof rArrString>;
1082+
expectType<{
1083+
fooArray: string[]
1084+
}>({} as rTArrString);
1085+
// Readonly array of numbers using string definition
1086+
const rArrNum = new Schema({
1087+
fooArray: {
1088+
type: [{
1089+
type: 'Number',
1090+
required: true
1091+
}] as const,
1092+
required: true
1093+
}
1094+
});
1095+
type rTArrNum = InferSchemaType<typeof rArrNum>;
1096+
expectType<{
1097+
fooArray: number[]
1098+
}>({} as rTArrNum);
1099+
// Readonly array of object with key named "type"
1100+
const rArrType = new Schema({
1101+
fooArray: {
1102+
type: [{
1103+
type: {
1104+
type: String,
1105+
required: true
1106+
},
1107+
foo: {
1108+
type: Number,
1109+
required: true
1110+
}
1111+
}] as const,
1112+
required: true
1113+
}
1114+
});
1115+
type rTArrType = InferSchemaType<typeof rArrType>;
1116+
expectType<{
1117+
fooArray: {
1118+
type: string;
1119+
foo: number;
1120+
}[]
1121+
}>({} as rTArrType);
1122+
}

types/inferschematype.d.ts

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -152,17 +152,15 @@ type OptionalPaths<T, TypeKey extends string = DefaultTypeKey> = {
152152

153153
/**
154154
* @summary Obtains schema Path type.
155-
* @description Obtains Path type by calling {@link ResolvePathType} OR by calling {@link InferSchemaType} if path of schema type.
155+
* @description Obtains Path type by separating path type from other options and calling {@link ResolvePathType}
156156
* @param {PathValueType} PathValueType Document definition path type.
157157
* @param {TypeKey} TypeKey A generic refers to document definition.
158158
*/
159-
type ObtainDocumentPathType<PathValueType, TypeKey extends string = DefaultTypeKey> = PathValueType extends Schema<any>
160-
? InferSchemaType<PathValueType>
161-
: ResolvePathType<
162-
PathValueType extends PathWithTypePropertyBaseType<TypeKey> ? PathValueType[TypeKey] : PathValueType,
163-
PathValueType extends PathWithTypePropertyBaseType<TypeKey> ? Omit<PathValueType, TypeKey> : {},
164-
TypeKey
165-
>;
159+
type ObtainDocumentPathType<PathValueType, TypeKey extends string = DefaultTypeKey> = ResolvePathType<
160+
PathValueType extends PathWithTypePropertyBaseType<TypeKey> ? PathValueType[TypeKey] : PathValueType,
161+
PathValueType extends PathWithTypePropertyBaseType<TypeKey> ? Omit<PathValueType, TypeKey> : {},
162+
TypeKey
163+
>;
166164

167165
/**
168166
* @param {T} T A generic refers to string path enums.
@@ -179,8 +177,29 @@ type PathEnumOrString<T extends SchemaTypeOptions<string>['enum']> = T extends R
179177
*/
180178
type ResolvePathType<PathValueType, Options extends SchemaTypeOptions<PathValueType> = {}, TypeKey extends string = DefaultSchemaOptions['typeKey']> =
181179
PathValueType extends Schema ? InferSchemaType<PathValueType> :
182-
PathValueType extends (infer Item)[] ? IfEquals<Item, never, any[], Item extends Schema ? Types.DocumentArray<ObtainDocumentPathType<Item, TypeKey>> : ObtainDocumentPathType<Item, TypeKey>[]> :
183-
PathValueType extends ReadonlyArray<infer Item> ? IfEquals<Item, never, any[], Item extends Schema ? Types.DocumentArray<ObtainDocumentPathType<Item, TypeKey>> : ObtainDocumentPathType<Item, TypeKey>[]> :
180+
PathValueType extends (infer Item)[] ?
181+
IfEquals<Item, never, any[], Item extends Schema ?
182+
// If Item is a schema, infer its type.
183+
Types.DocumentArray<InferSchemaType<Item>> :
184+
Item extends Record<TypeKey, any>?
185+
Item[TypeKey] extends Function | String ?
186+
// If Item has a type key that's a string or a callable, it must be a scalar,
187+
// so we can directly obtain its path type.
188+
ObtainDocumentPathType<Item, TypeKey>[] :
189+
// If the type key isn't callable, then this is an array of objects, in which case
190+
// we need to call ObtainDocumentType to correctly infer its type.
191+
ObtainDocumentType<Item, any, { typeKey: TypeKey }>[]:
192+
ObtainDocumentPathType<Item, TypeKey>[]
193+
>:
194+
PathValueType extends ReadonlyArray<infer Item> ?
195+
IfEquals<Item, never, any[], Item extends Schema ?
196+
Types.DocumentArray<InferSchemaType<Item>> :
197+
Item extends Record<TypeKey, any> ?
198+
Item[TypeKey] extends Function | String ?
199+
ObtainDocumentPathType<Item, TypeKey>[] :
200+
ObtainDocumentType<Item, any, { typeKey: TypeKey }>[]:
201+
ObtainDocumentPathType<Item, TypeKey>[]
202+
>:
184203
PathValueType extends StringSchemaDefinition ? PathEnumOrString<Options['enum']> :
185204
IfEquals<PathValueType, Schema.Types.String> extends true ? PathEnumOrString<Options['enum']> :
186205
IfEquals<PathValueType, String> extends true ? PathEnumOrString<Options['enum']> :

0 commit comments

Comments
 (0)