一起养成写作习惯!这是我参与「掘金日新计划 · 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;
直到最开始T和K都等于 1 的时候,这里省略了部分推导,主要是看第一个类型获得的结果:
[1] extends [never]
? []
: 1 extends 1
? [1, ...[2, ...[3, ...[]]]]
: never;
// T 和 K 的第一个类型都推断完了后,则结果就是
[1, 2, 3]
主要就是利用分布类型来对联合类型进行迭代,这样就能达到全排列组合。
如果你能理解这个条件分布类型是怎么处理的,那么下面这个表就能看懂了:
type P = Permutation;
type X = Exclude
| Iteration | T | K in K extends K | X<T, K> | [K, ...P<X<T, K>>] | Result | ||||
|---|---|---|---|---|---|---|---|---|---|
| 1 | 1 | 2 | 3 | 1 | 2 | 3 | [1, ...P<2 | 3>] | |
| 1.1 | 2 | 3 | 2 | 3 | [1, 2, ...P<3>] | ||||
| 1.1.1 | 3 | 3 | never | [1, 2, 3, ...[]] | [1, 2, 3] | ||||
| 1.2 | 2 | 3 | 3 | 2 | [1, 3, ...P<2>] | ||||
| 1.2.1 | 2 | 2 | never | [1, 3, 2, ...[]] | [1, 3, 2] | ||||
| 2 | 1 | 2 | 3 | 2 | 1 | 3 | [2, ...P<1 | 3>] | |
| 2.1 | 1 | 3 | 1 | 3 | [2, 1, ...P<3>] | ||||
| 2.1.1 | 3 | 3 | never | [2, 1, 3, ...[]] | [2, 1, 3] | ||||
| 2.2 | 1 | 3 | 3 | 1 | [2, 3, ...P<1>] | ||||
| 2.2.1 | 1 | 1 | never | [2, 3, 1, ...[]] | [2, 3, 1] | ||||
| 3 | 1 | 2 | 3 | 3 | 1 | 2 | [3, ...P<1 | 2>] | |
| 3.1 | 1 | 2 | 1 | 2 | [3, 1, ...P<2>] | ||||
| 3.1.1 | 2 | 2 | never | [3, 1, 2, ...[]] | [3, 1, 2] | ||||
| 3.2 | 1 | 2 | 2 | 1 | [3, 2, ...P<1>] | ||||
| 3.2.1 | 1 | 1 | never | [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'];
首先需要明确一点,只有元组类型才可以推断出具体的长度,因为元组中的元素个数是已知的。
使用模板字符串进行推断是可以获得字符串的第一个字符的,所以依赖这个特性,将字符串遍历到底,并将每个字符放到一个数组中,这样当循环结束,就可以通过元组类型获取长度了。