ts-challenges 实现联合类型的全排列Permutation

440 阅读3分钟

题目

实现联合类型的全排列,将联合类型转换成所有坑呢的全排列数组的联合类型。

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 cases = [    Expect<Equal<Permutation<'A'>, ['A']>>,
    Expect<Equal<Permutation<'A' | 'B' | 'C'>, ['A', 'B', 'C'] | ['A', 'C', 'B'] | ['B', 'A', 'C'] | ['B', 'C', 'A'] | ['C', 'A', 'B'] | ['C', 'B', 'A']>>,
    Expect<Equal<Permutation<boolean>, [false, true] | [true, false]>>,
    Expect<Equal<Permutation<never>, []>>
]

解答区

type Permutation<T> = any

答案

type Permutation<T, K=T> = [T] extends [never] 
    ? [] 
    : K extends K 
        ? [K, ...Permutation<Exclude<T, K>>]
        : never

解题思路

看题目给出来的例子,第一个和最后一个还是比较简单的。

也就是

image.png

当只有一个参数,且是字符串的时候,就直接输出自己的数组这种案例;当只有一个参数,且是never的话,直接输出一个空数组。

那直接写成:

type Permutation<T> = T extends never ? [] : [T]

这样可以吗?我们可以看到结果如下:

image.png

只有第一种情况红色波浪线去除了,说明验证成功,那么最后一种情况never为什么还是不通过呢?我们来检验一下。

截屏2022-10-10下午3.22.14.png

当我们的写法为T extends never ? [] : XX这种写法时,type a = Permutation<never> 得出的结果直接是type a = never, 假设一下:“只要这个参数传进去是never,那么得到的结果就是never?”显然不是,不是参数变量传了never,返回来的结果就never。下面这个举例可以推翻。也就是直接返回'a', 结果得到的就是'a'。

截屏2022-10-10下午3.27.08.png

那么再来假设一下:“当 变量 extends never的时候,直接返回never?”

截屏2022-10-10下午3.30.10.png

由这个举证可以看出,“当变量 extends never时,也就是 T extends never 时, 结果直接返回never”。这个例子就是也不走'a' ,也 不走'b',而是 T extends never 直接返回了never。为什么呢?我们来看一下。

第一点、 T extends never 直接返回never

提问issue的链接

IsNeverType 需要这样实现:

type IsNeverType<T> = T extends never ? true : never

// typescript 把 `never` 当成一个空的联合值, 所以 T extends never 这个结果最后是never,既不是true也不是false。所以我们要改动一些写法,让这个T能够被捕获到,下面👇这种写法就能轻松解决这个问题。
// 换成这种写法,当T为never时,可以被捕获到。
// 将`T`放入元组: [T]
type IsNeverType<T> = [T] extends [never] ? true : never
// 或者把T转成数组: T[]

type IsNeverType<T> = T[] extends never[] ? truenever

第二点、K extends K

现在传参类型当中定义一个K类型,这个K就是等于T。

类型联合在条件上分布,也就是说:联合类型 'a' | 'b' | 'c' 会被递归循环 'a'、'b'、'c'三次。

如果 K extends K 就 [K, ...Permutation<Exclude<T, K>>] 
type P = Permutation;
type X = Exclude

// 记住 P<never> => []

来看一下当传参联合类型为 : P<1 | 2 | 3>, [K, ...Permutation<Exclude<T, K>>]这一段代码发生了什么

循环TK in K extends KX<T, K>[K, ...P<X<T, K>>]结果
11|2|312|3[1, ...P<2 | 3>]
1.12|323[1, 2, ...P<3>]
1.1.133never[1, 2, 3, ...[]][1, 2, 3]
1.22|332[1, 3 ...P<2>]
1.2.122never[1, 3, 2, ...[]][1, 3, 2]
21|2|321|3[2, ...P<1 | 3>]
2.11|313[2, 1, ...P<3>]
2.1.133never[2, 1, 3, ...[]][2, 1, 3]
2.21|331[2, 3 ...P<1>]
2.2.211never[2, 3, 1, ...[]][2, 3, 1]
31|2|331|2[3, ...P<1 | 2>]
3.11|212[3, 1, ...P<2>]
3.1.122never[3, 1, 2, ...[]][3, 1, 2]
3.21|221[3, 2, ...P<1>]
3.2.211never[3, 2, 1, ...[]][3, 2, 1]

所以P<1 | 2 | 3>最终有的组合是: [1, 2, 3] | [1, 3, 2] | [2, 1, 3] | [2, 3, 1] | [3, 1, 2] | [3, 2, 1]

最终答案type Permutation 可以实现联合类型的全排列:

type Permutation<T, K=T> = [T] extends [never] 
    ? [] 
    : K extends K 
        ? [K, ...Permutation<Exclude<T, K>>]
        : never

截屏2022-10-11下午4.44.30.png

至此,此中等题目之实现Permutation(实现联合类型的全排列)完成!

参考链接