I've found it frequently desirable to be able to "look up" the union variant associated with a type tag. For example, given a union
type Foo = { tag: 'n'; val: number } | { tag: 's'; val: string }
one wants to be able to write a type operator
type Lookup<T extends Foo['tag']> = // ???
such that Lookup<'n'> = number and Lookup<'s'> = string.
I don't think this is possible in the current type system (would welcome correction on that point). Instead, we can use the trick of starting with a Lookup type, and deriving the union type from it:
type Lookup = {
n: number
s: string
}
type Foo = {
[T in keyof Lookup]: { tag: T; val: Lookup[T] }
}[keyof Lookup]
then the inferred type of Foo is
type Foo = {
tag: "n";
val: number;
} | {
tag: "s";
val: string;
}
as desired, and we have our lookup type: Lookup['n'] = number and Lookup['s'] = string.
So I use this pattern a lot, and I wanted to generalize it:
type Unionize<Lookup> = {
[T in keyof Lookup]: { tag: T; val: Lookup[T] }
}[keyof Lookup]
Unfortunately, this doesn't do what you want:
type Foo = Unionize<{
n: number
s: string
}>
Here, Foo is inferred to be
type Foo = {
tag: "n" | "s";
val: string | number;
}
This seems like a bug!
I've found it frequently desirable to be able to "look up" the union variant associated with a type tag. For example, given a union
one wants to be able to write a type operator
such that
Lookup<'n'>=numberandLookup<'s'>=string.I don't think this is possible in the current type system (would welcome correction on that point). Instead, we can use the trick of starting with a
Lookuptype, and deriving the union type from it:then the inferred type of
Fooisas desired, and we have our lookup type:
Lookup['n']=numberandLookup['s']=string.So I use this pattern a lot, and I wanted to generalize it:
Unfortunately, this doesn't do what you want:
Here,
Foois inferred to beThis seems like a bug!