TypeScript类型体操挑战(七)

136 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第3天,点击查看活动详情

中等

Permutation

挑战要求

在线示例

type Permutation<T, K = T> = 
  [T] extends [never]
  ? []
  : K extends K 
    ? [K, ...Permutation<Exclude<T, K>>]
    : never;
  • 为什么是[T] extends [never]而不是用T extends never呢?

由于泛型作用于条件类型的时候,会发生分布,而never很多时候我们是用来排除掉某个类型的。

看个例子:

type Exclude<T, U> = T extends U ? never : T;

type T = Exclude<'a' | 'b', 'a'>;
// 相当于
type T = 'a' extends 'a' ? never : 'a' | 'b' extends 'a' ? never : 'b';
type T = never | 'b'

never是永远不存在的值,最后的结果是type T = 'b'

所以在使用Permutation的时候,当发生分布时,never类型的值是捕捉不到的,所以得在发生分布前确定T的值为never,方法有两种:

// 元组
[T] extends [never]

// 数组
T[] extends never[]

再来看看K extends K ? [K, ...Permutation<Exclude<T, K>>] : never这段代码。

首先一定要记住这一点:当条件类型作用于泛型类型时,当给定一个联合类型时,它们就变成了分布型

直接通过示例来看:假如有一个Permutation<1 | 2 | 3>,由于T为一个联合类型,所以发生了分布:

/*
  T = 1 | 2 | 3
  T 就先从 1 开始吧
*/
[1] extends [never]
? []
: K extends K
  ? [K, ...Permutation<Exclude<T, K>>]
  : never;
     
/*
  K = 1 | 2 | 3
  由于 K extends K,又会触发分布,那么 K 就先从 1 开始吧
*/
[1] extends [never]
? []
: 1 extends 1
  ? [1, ...Permutation<Exclude<1 | 2 | 3, 1>>]
  : never;

按照条件判断就会进入[K, ...Permutation<Exclude<T, K>>],就会有[1, ...Permutation<2 | 3>]

Permutation<2 | 3>又会触发分布:

/*
  T = 2 | 3
  K = 2 | 3
  T 和 K 就先从 2 开始吧(K = 3 的时候这个我就先忽略了,后面也是直接给出结果)
*/
[2] extends [never]
? []
: 2 extends 2 
  ? [2, ...Permutation<Exclude<2 | 3, 2>>]
  : never;

按照条件判断就会进入[K, ...Permutation<Exclude<T, K>>],就会有[2, ...Permutation<3>]

/*
  T = 3
  K = 3
*/
[3] extends [never]
? []
: 3 extends 3 
  ? [3, ...Permutation<Exclude<3, 3>>]
  : never;

按照条件判断就会进入[K, ...Permutation<Exclude<T, K>>],就会有[3, ...Permutation<never>],然后结果就会是:

[3] extends [never]
? []
: 3 extends 3 
  ? [3, ...[]]
  : never;

好了,继续往上返回:

[2] extends [never]
? []
: 2 extends 2 
  ? [2, ...[3, ...[]]]
  : never;

直到最开始TK都等于 1 的时候,这里省略了部分推导,主要是看第一个类型获得的结果:

[1] extends [never]
? []
: 1 extends 1
  ? [1, ...[2, ...[3, ...[]]]]
  : never;

// T 和 K 的第一个类型都推断完了后,则结果就是
[1, 2, 3]

主要就是利用分布类型来对联合类型进行迭代,这样就能达到全排列组合。

如果你能理解这个条件分布类型是怎么处理的,那么下面这个表就能看懂了:

type P = Permutation;
type X = Exclude
IterationTK in K extends KX<T, K>[K, ...P<X<T, K>>]Result
1123123[1, ...P<23>]
1.12323[1, 2, ...P<3>]
1.1.133never[1, 2, 3, ...[]][1, 2, 3]
1.22332[1, 3, ...P<2>]
1.2.122never[1, 3, 2, ...[]][1, 3, 2]
2123213[2, ...P<13>]
2.11313[2, 1, ...P<3>]
2.1.133never[2, 1, 3, ...[]][2, 1, 3]
2.21331[2, 3, ...P<1>]
2.2.111never[2, 3, 1, ...[]][2, 3, 1]
3123312[3, ...P<12>]
3.11212[3, 1, ...P<2>]
3.1.122never[3, 1, 2, ...[]][3, 1, 2]
3.21221[3, 2, ...P<1>]
3.2.111never[3, 2, 1, ...[]][3, 2, 1]

答案参考自解答区,这个大佬说的很详细,不过都是英文。。。

Length of String

挑战要求

在线示例

type LengthOfString<S extends string, K extends any[] = []> = 
  S extends `${infer F}${infer E}`
  ? LengthOfString<E, [...K, F]>
  : K['length'];

首先需要明确一点,只有元组类型才可以推断出具体的长度,因为元组中的元素个数是已知的。

使用模板字符串进行推断是可以获得字符串的第一个字符的,所以依赖这个特性,将字符串遍历到底,并将每个字符放到一个数组中,这样当循环结束,就可以通过元组类型获取长度了。