For example i have type
type abc = \'a\' | \'b\' | \'c\';
How to make tuple type that contains all elements of the union in compile time?
I've sometimes faced a situation in which I want to derive type B from type A but find that either TS does not support it, or that doing the transformation results in code that's hard to follow. Sometimes, the choice of deriving B from A is arbitrary and I could just as well derive in the other direction. Here if you can start with your tuple, you can easily derive a type that covers all the values that the tuple accepts as elements:
type X = ["a", "b", "c"];
type AnyElementOf<T extends any[]> = T[number];
type AnyElementOfX = AnyElementOf<X>;
If you inspect the expansion of AnyElementOfX
you'll get "a" | "b" | "c"
.
It's easy to convert from a tuple type to a union type; for example, see this question. But the opposite, converting from a union to a tuple is one of those Truly Bad Ideas that you shouldn't try to do. Let's do it first and scold ourselves later:
// oh boy don't do this
type UnionToIntersection<U> =
(U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never
type LastOf<T> =
UnionToIntersection<T extends any ? () => T : never> extends () => (infer R) ? R : never
// TS4.0+
type Push<T extends any[], V> = [...T, V];
// TS4.1+
type TuplifyUnion<T, L = LastOf<T>, N = [T] extends [never] ? true : false> =
true extends N ? [] : Push<TuplifyUnion<Exclude<T, L>>, L>
type abc = 'a' | 'b' | 'c';
type t = TuplifyUnion<abc>; // ["a", "b", "c"]
Playground link
That kind of works, but I really really REALLY recommend not using it for any official purpose or in any production code. Here's why:
You can't rely on the ordering of a union type. It's an implementation detail of the compiler; since X | Y
is equivalent to Y | X
, the compiler feels free to change one to the other. And sometimes it does:
type TypeTrue1A = TuplifyUnion<true | 1 | "a">; // [true, 1, "a"]