One potential solution could be as follows. While not extensively tested, it appears to be functional.
type Union =
{ type: 'A', a: string } |
{ type: 'B', b: number }
type MappedUnion = {[P in Union['type']]: Union & {type: P}};
const v: MappedUnion = {
'A': {type: 'A', a: "hello"},
'B': {type: 'B', b: 3},
}
Unfortunately, this approach falls short; for example:
type MappedUnionTarget = {
A: { type: 'A', a: string }
B: { type: 'B', b: number }
}
function cvt1(x: MappedUnion): MappedUnionTarget {
return x;
}
The resulting error message states
Type '({ type: "A"; a: string; } & { type: "A"; }) | ({ type: "B"; b: number; } & { type: "A"; })' is not assignable to type '{ type: "A"; a: string; }'
. It was expected that TypeScript would recognize that
({ type: "B"; b: number; } & { type: "A"; })
should equate to
never
.
If you're open to utilizing Typescript 2.8 (not yet officially released, but obtainable through a git pull
from the TypeScript repository), which introduces support for the conditional operator ? :
at the type level, consider the following workaround:
type FilterType<T, K> = (T extends { type: K } ? T : never);
type MappedUnion = {[P in Union['type']]: FilterType<Union, P>};
This modified implementation of FilterType<T, K>
aims to replicate the behavior initially anticipated with T & {type: K}
.