Creating a discriminated union using the property names of another type
typescript
Solution
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.
Problem
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.