Suppose there is a union type Thing
grouping together types Foo
, Bar
and Baz
with the discriminating property tag
.
interface Foo {
tag: 'Foo'
foo: string
}
interface Bar {
tag: 'Bar'
bar: number
}
interface Baz {
tag: 'Baz'
baz: boolean
}
type Union = Foo | Bar | Baz
Now I would like to create a mapped type where I would iterate over tags of Union
and use the corresponding interface in the type mapped to the tag. The question is: Is it possible to retrieve a type from a union type by its tag value?
interface Tagged {
tag: string
}
type TypeToFunc<U extends Tagged> = {
// Is it possilbe to retrieve the type for the given tag from the union type?
// What to put in place of the ???
readonly [T in U['tag']]: (x: ???) => string
}
const typeToFunc: TypeToFunc<Union> = {
// x must be of type Foo
Foo: x => `FOO: ${x.foo}`,
// x must be of type Bar
Bar: x => `BAR: ${x.bar}`,
// x must be of type Baz
Baz: x => `BAZ: ${x.baz}`,
}
If not, is there any other way to achieve this kind of mapping?
In TypeScript v2.7 and earlier, there is no programmatic way to do this. It is easier to have TypeScript build unions programmatically than it is to inspect them. Therefore, you could do this instead:
Now you can use
Union
as you did before, but the individual union constituents can be referred to asUnion<'Foo'>
,Union<'Bar'>
, andUnion<'Baz'>
. For convenience you can still give them names:And type your function like this:
Starting in TypeScript v2.8, there will be a feature called conditional types which allows a lot more expressivity in the type system. You can write a general union discriminator like this:
And then, with your original definitions:
You get the almost magical:
which also works. You can try this out now if you install
typescript@next
fromnpm
... otherwise you'll need to wait.Hope that helps; good luck!