Typescript类型体操 - Permutation
题目
中文
实现联合类型的全排列,将联合类型转换成所有可能的全排列数组的联合类型。
type perm = Permutation<'A' | 'B' | 'C'>; // ['A', 'B', 'C'] | ['A', 'C', 'B'] | ['B', 'A', 'C'] | ['B', 'C', 'A'] | ['C', 'A', 'B'] | ['C', 'B', 'A']
English
Implement permutation type that transforms union types into the array that includes permutations of unions.
type perm = Permutation<'A' | 'B' | 'C'>; // ['A', 'B', 'C'] | ['A', 'C', 'B'] | ['B', 'A', 'C'] | ['B', 'C', 'A'] | ['C', 'A', 'B'] | ['C', 'B', 'A']
答案
type Permutation<T, K = T> = [T] extends [never]
? []
: K extends K
? [K, ...Permutation<Exclude<T, K>>]
: never;
解析
这个题目挺有意思的,要你把联合类型转成一个由数组构成的联合类型,要求输出的数组联合类型是输入的联合类型的所有可能的排列组合,看示例比较容易理解
type Perm = Permutation<'A' | 'B' | 'C'>;
// Perm的类型应该是 -> ['A', 'B', 'C'] | ['A', 'C', 'B'] | ['B', 'A', 'C'] | ['B', 'C', 'A'] | ['C', 'A', 'B'] | ['C', 'B', 'A']
答案中的K extends K
的是遍历联合类型
type TraversalUnion<T extends string> = T extends T ? `Traversaled ${T extends string ? T : never}` : never;
type T = TraversalUnion<'a' | 'b'>
// T的类型 -> "Traversaled a" | "Traversaled b"
理解了这点,就可以去递归了
K extends K
? [K, ...Permutation<Exclude<T, K>>]
: never
[K, ...Permutation<Exclude<T, K>>]
这个子句中,第一个元素是通过K extends K
遍历出来的一个联合类型, 第二个元素递归调用了Permutation
,且使用了一个数组展开运算符(...
), 它的参数Exclude<T, K>
, 在Permutation
的定义中泛型参数T
是等于K
的,子句中的K
的遍历出来的一个子项,T
是完整的联合类型,很巧妙的就递归下去了;
最后回到第一个子句
[T] extends [never]
? []
...
这个子句其实就是递归的终止条件,上面递归的子句中使用了Exclude
, 所以递归到最后传给Permutation
的就是个never
(Exclude两个泛型参数相同时,返回的就是never)