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)

posted @ 2022-09-06 00:12  Laggage  阅读(77)  评论(0编辑  收藏  举报