I am trying to make a generic type that takes the property names and property types from a type and use them to make a discriminated union type. For example:
type FooBar = {
foo: string;
bar: number;
};
would map to:
type FooBarPair = {
key: "foo",
value: string
} | {
key: "bar",
value: number
}
My initial attempt is:
type Pairs<T> = {
[TKey in keyof T]: {
key: TKey;
value: T[TKey];
};
};
type Pair<T> = Pairs<T>[keyof T];
but when I try to apply this to the type above:
let pair: Pair<FooBar> = {
key: "foo",
value: 3
};
I would expect a compile error but there isn't one. When I inspect the type of Pair<FooBar>
I find it equates to:
{
key: "foo" | "bar";
value: string | number;
}
I think this might be a bug in typescript but I wanted to see if there was some other way to achieve this.
Yes there is a way to achieve this. It turns out that Typescript infers the desired type
`{ key: "foo"; value: string; } | { key: "bar"; value: number; }`
when you remove intermediate generic type type Pair<T> = Pairs<T>[keyof T];
and expand it inline like Pairs<FooBar>[keyof FooBar]
every time you would have used it:
type Pairs<T> = {
[TKey in keyof T]: {
key: TKey;
value: T[TKey];
};
};
type FooBar = {
foo: string;
bar: number;
};
let pair: Pairs<FooBar>[keyof FooBar] = {
key: "foo",
value: 3
};
the complete error message is
Type '{ key: "foo"; value: number; }' is not assignable to type '{ key: "foo"; value: string; } | { key: "bar"; value: number; }'.
Type '{ key: "foo"; value: number; }' is not assignable to type '{ key: "foo"; value: string; }'.
Types of property 'value' are incompatible.
Type 'number' is not assignable to type 'string'.
I'm not sure what's the "correct" type to infer in either case, but introduction of shorthand type like Pair<T> = Pairs<T>[keyof T]
should not affect type inference IMO. Seems like a bug.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With