Reputation: 9286
Let's start with this given type:
type Union =
{ type: 'A', a: string } |
{ type: 'B', b: number }
I want us to end up here:
type MappedUnion = {
A: { type: 'A', a: string }
B: { type: 'B', b: number }
}
Here's some pseudo code:
type MappedUnion<item in Union> = {[ i: item['type'] ]: item}
Upvotes: 2
Views: 171
Reputation: 26
I think you can do the following? Haven't tested exhaustively, but it seems to work.
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},
}
Sadly, doesn't quite work; If I do
type MappedUnionTarget = {
A: { type: 'A', a: string }
B: { type: 'B', b: number }
}
function cvt1(x: MappedUnion): MappedUnionTarget {
return x;
}
then I get Type '({ type: "A"; a: string; } & { type: "A"; }) | ({ type: "B"; b: number; } & { type: "A"; })' is not assignable to type '{ type: "A"; a: string; }'.
I thought typescript would be able to conclude that ({ type: "B"; b: number; } & { type: "A"; })
was the same as never
.
If you're willing to use Typescript 2.8 (which at the time of writing is unreleased and available if you git pull
from the TypeScript repo, and which has support for the conditional operator ? :
at the type level) then it seems you can do
type FilterType<T, K> = (T extends { type: K } ? T : never);
type MappedUnion = {[P in Union['type']]: FilterType<Union, P>};
because FilterType<T, K>
is the thing that I had hoped T & {type: K}
to behave like.
Upvotes: 1