How to transform union type to tuple type

后端 未结 2 1780
半阙折子戏
半阙折子戏 2020-11-27 08:42

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?

相关标签:
2条回答
  • 2020-11-27 09:04

    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".

    0 讨论(0)
  • 2020-11-27 09:12

    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"]                                                                     
    0 讨论(0)
提交回复
热议问题