REVISION FOR TS2.8+
Ever since the introduction of conditional types, streamlining the process of strongly typing theMap
has become much simpler. Utilizing Extract<U, X>
, we can now extract only those elements from a union type U
that are assignable to X
:
type Union = { type: "A"; a: string } | { type: "B"; b: number };
const theMap: {
[K in Union["type"]]: (u: Extract<Union, { type: K }>) => string
} = {
A: ({ a }) => "this is a: " + a,
B: ({ b }) => "this is b: " + b
};
This method is straightforward. Unfortunately, as of TS2.7 or later, calling theMap(u.type)(u)
is no longer permitted by the compiler. The relationship between the function theMap(u.type)
and value u
is considered 'correlated', but this correlation goes unnoticed by the compiler. Instead, it treats theMap(u.type)
and u
as independent union types, necessitating a type assertion for one to call the other:
const handle = (u: Union): string =>
(theMap[u.type] as (v: Union) => string)(u); // this assertion is necessary
Alternatively, you could manually navigate through potential union values like so:
const handle = (u: Union): string =>
u.type === "A" ? theMap[u.type](u) : theMap[u.type](u); // redundant
I usually recommend using assertions for such cases.
An open issue addressing these correlated types exists, although the possibility of future support remains uncertain. Best of luck with your endeavors!
Answer for TS2.7 and earlier:
With the given definition of the Union
type, TypeScript struggles to simultaneously enforce both an exhaustive check on theMap
(ensuring there's exactly one handler for each constituent type of the union) and a soundness constraint (each handler within theMap
corresponds to a specific constituent type of the union).
However, utilizing a more general type allows us to express and maintain these constraints effectively. Let's first explore the generalized type:
type BaseTypes = {
A: { a: string };
B: { b: number };
}
...
That was quite a bit to digest, but hopefully, it provides some clarity. Best of luck moving forward!