例如我有类型
type abc = 'a' | 'b' | 'c';
Run Code Online (Sandbox Code Playgroud)
如何使元组类型在编译时包含联合的所有元素?
type t = ['a','b', 'c'];
Run Code Online (Sandbox Code Playgroud)
这是您不应该尝试做的“真正坏主意”之一。让我们先做,然后责骂自己:
// 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
type PushTuple = [[0], [0, 0], [0, 0, 0],
[0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
];
type Push<
T extends any[],
V,
L = PushTuple[T['length']],
P = { [K in keyof L]: K extends keyof T ? T[K] : V }
> = P extends any[] ? P : never;
type TuplifyUnion<T, L=LastOf<T>, N=[T] extends [never] ? true : false> = true extends N ? [] : Push<TuplifyUnion1<Exclude<T, L>>, L>
type TuplifyUnion1<T, L=LastOf<T>, N=[T] extends [never] ? true : false> = true extends N ? [] : Push<TuplifyUnion2<Exclude<T, L>>, L>
type TuplifyUnion2<T, L=LastOf<T>, N=[T] extends [never] ? true : false> = true extends N ? [] : Push<TuplifyUnion3<Exclude<T, L>>, L>
type TuplifyUnion3<T, L=LastOf<T>, N=[T] extends [never] ? true : false> = true extends N ? [] : Push<TuplifyUnion4<Exclude<T, L>>, L>
type TuplifyUnion4<T, L=LastOf<T>, N=[T] extends [never] ? true : false> = true extends N ? [] : Push<TuplifyUnion5<Exclude<T, L>>, L>
type TuplifyUnion5<T, L=LastOf<T>, N=[T] extends [never] ? true : false> = true extends N ? [] : Push<TuplifyUnion6<Exclude<T, L>>, L>
type TuplifyUnion6<T, L=LastOf<T>, N=[T] extends [never] ? true : false> = true extends N ? [] : Push<TuplifyUnion7<Exclude<T, L>>, L>
type TuplifyUnion7<T, L=LastOf<T>, N=[T] extends [never] ? true : false> = true extends N ? [] : Push<TuplifyUnion8<Exclude<T, L>>, L>
type TuplifyUnion8<T, L=LastOf<T>, N=[T] extends [never] ? true : false> = true extends N ? [] : Push<TuplifyUnion9<Exclude<T, L>>, L>
type TuplifyUnion9<T, L=LastOf<T>, N=[T] extends [never] ? true : false> = true extends N ? [] : Push<TuplifyUnionX<Exclude<T, L>>, L>
type TuplifyUnionX<T> = never
type abc = 'a' | 'b' | 'c';
type t = TuplifyUnion<abc>; // ["a", "b", "c"]
Run Code Online (Sandbox Code Playgroud)
这种工作,但我真的真的建议不要将其用于任何官方目的或任何生产代码中。原因如下:
您不能依赖联合类型的顺序。这是编译器的实现细节;因为X | Y
等效于Y | X
,所以编译器可以自由更改一个。有时它会:
type TypeTrue1A = TuplifyUnion<true | 1 | "a">; // [true, 1, "a"]
type Type1ATrue = TuplifyUnion<1 | "a" | true>; // [true, 1, "a"]!!
Run Code Online (Sandbox Code Playgroud)
因此,实际上没有办法保留订单。
您不能依赖于编译器认为的联合以及联合何时折叠或展开。 "a" | string
只会折叠到string
,boolean
实际上会扩展为false | true
:
type TypeAString = TuplifyUnion<"a" | string>; // [string]
type TypeBoolean = TuplifyUnion<boolean>; // [false, true]
Run Code Online (Sandbox Code Playgroud)
因此,如果您打算保留一些现有元素,则应停止进行规划。没有一种通用的方法可以让元组进入工会并返回而又不会丢失此信息。
没有支持的方法可以通过大联盟进行迭代。我正在使用所有滥用条件类型的技巧。首先,我将一个并集A | B | C
转换为一个函数的合并,例如()=>A | ()=>B | ()=>C
,然后使用交集推论技巧将该函数的合并转换为一个函数的交集,例如()=>A & ()=>B & ()=>C
,它被解释为单个重载函数类型,并使用条件类型提取返回值仅捕获最后的重载。所有这些疯狂最终A | B | C
可能只是拿走了一个组成部分C
。然后,您必须将其推入要构建的元组的末尾,然后……遇到了这个问题:
通过不支持对象键集的一组事物进行类型迭代。显而易见的解决方案导致了循环类型别名,这是被禁止的。您可以使用邪恶的技巧来欺骗编译器,使其不注意您的递归类型别名,但是对此一无所知。我已经尝试过了,但从来没有取得好成绩。因此,唯一不会使编译器爆炸的方法是选择一个可以工作的最大有限元组长度(例如10),然后将正常的递归定义展开到那么多几乎多余的行中。
所以你去了。您可以做到这一点,但不要这样做。(如果您这样做,如果发生爆炸,请不要怪我。)希望能有所帮助。祝好运!