14

Is it possible to map a union type to another union type in TypeScript?

What I'd Like to be able to do

e.g. Given a union type A:

type A = 'one' | 'two' | 'three';

I'd like to be able to map it to union type B:

type B = { type: 'one' } | { type: 'two'} | { type: 'three' };

What I have tried

type B = { type: A };

But this results in:

type B = { type: 'one' | 'two' | 'three' };

which is not quite what I want.

bingles
  • 9,662
  • 6
  • 65
  • 76

1 Answers1

25

You can use conditional type for distributing over the members of the union type (conditional type always takes only one branch and is used only for its distributive property, I learned this method from this answer)

type A = 'one' | 'two' | 'three';

type Distribute<U> = U extends any ? {type: U} : never;

type B = Distribute<A>;

/*
type B = {
    type: "one";
} | {
    type: "two";
} | {
    type: "three";
}
*/
artem
  • 29,391
  • 6
  • 61
  • 63
  • This worked. Out of curiosity how does the `U extends {}` resolve as truthy to provide { type: U } ? – bingles Aug 05 '18 at 15:37
  • `U extends {}` is true for object types, "value" types like `string`, `number` and `boolean` and literals of those value types. It's false if `U` is `undefined` and `void`, so the statement "always takes only one branch" is not true, `U extends {}` excludes `void` and `undefined` from the union. I updated the answer to change it to `U extends any` because I don't know if it's actually desirable to exclude types like `void` and `undefined`. – artem Aug 05 '18 at 15:49
  • Thanks for the clarification. I was suprised that string literals extend {} – bingles Aug 06 '18 at 11:10