New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Impossible to Describe
generic types with conditional
#1163
Comments
I think I narrowed down the main (two) issue(s). type Validating<T extends Types = Types> = {
type: T;
value: T extends Types.One ? string : never;
};
const Validating: Describe<Validating> = object({
type: enums(Object.values(Types)),
// eslint-disable-next-line @typescript-eslint/no-explicit-any
value: dynamic((_, ctx): any => {
const type: Types = ctx.branch.at(-2)?.type ?? Types.One;
if (type === Types.One) {
return string();
} else {
return literal(undefined);
}
}),
}); This works, but requires you to set The second issue is when you make the value an type Validating<T extends Types = Types> = {
type: T;
value: T extends Types.One ? string[] : never;
};
const Validating: Describe<Validating> = object({
type: enums(Object.values(Types)),
// eslint-disable-next-line @typescript-eslint/no-explicit-any
value: dynamic((_, ctx): any => {
const type: Types = ctx.branch.at(-2)?.type ?? Types.One;
if (type === Types.One) {
return array(string());
} else {
return literal(undefined);
}
}),
}); This Gives you a cryptic type error, but this can be fixed by manually overriding the value type in the object like so: const Validating: Describe<Validating> = object({
type: enums(Object.values(Types)),
// eslint-disable-next-line @typescript-eslint/no-explicit-any
value: dynamic((_, ctx): any => {
const type: Types = ctx.branch.at(-2)?.type ?? Types.One;
if (type === Types.One) {
return array(string());
} else {
return literal(undefined);
}
}) as unknown as Describe<Validating["value"]>,
}); Still don't really have a nice fix for this, but hopefully this helps someone else facing the same issue. |
Could use Discriminated Union instead export function discriminatorMapping<
D extends string,
Mapping extends Record<string, TypeAny>
>(
discriminator: D,
mapping: Mapping
): Struct<Simplify<DiscriminatedUnion<D, Mapping>>, null> {
return dynamic<any>((v: any = {}) => {
const discriminatorValue = (v as any)[discriminator];
const matched = mapping[discriminatorValue];
if (typeof (v as any)[discriminator] === "undefined" || !matched) {
return object({
[discriminator]: enums(Object.keys(mapping))
});
}
return object({
[discriminator]: literal(discriminatorValue),
...matched.schema
});
});
}
type DiscriminatedUnion<
D extends string,
Mapping extends Record<string, TypeAny>
> = ValueOf<{
[K in keyof Mapping]: { [k in D]: K } & Infer<Mapping[K]>;
}>;
type ValueOf<T> = T[keyof T]; enum NetType {
AIRGAP = "AIRGAP",
DIRECT = "DIRECT",
}
const t: Struct<{ netType: NetType.AIRGAP } | { netType: NetType.DIRECT, endpoint: string }> = discriminatorMapping("netType", {
[NetType.AIRGAP]: object(),
[NetType.DIRECT]: object({
endpoint: string()
})
}
) |
I cannot figure out how to describe generics with conditionals. This might be a limitation by TypeScript though.
Given the following types:
I can't figure out how to type
value
inValidating
. This is my general idea:But this does not compile, giving the following error:
I also had a second idea which is even more broken, but is nicer if possible.
I think this is related to #724, but not quite the same.
The text was updated successfully, but these errors were encountered: