持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第5天,点击查看活动详情
中等
InorderTraversal
type InorderTraversal<T extends TreeNode | null> =
[T] extends [TreeNode]
?
[...InorderTraversal<T['left']>, T['val'], ...InorderTraversal<T['right']>]
:
[];
答案参考自解答区
最重要的就是这段代码:[T] extends [TreeNode],这里这么做是为了防止发生分布行为。
一开始我是这么做的:
type InorderTraversal<T extends TreeNode | null> =
T extends TreeNode
?
/*
这里报错:
// 可能无穷的。。。
Type instantiation is excessively deep and possibly infinite.(2589)
// 这句的大概意思是产生了一个复杂的联合类型
Expression produces a union type that is too complex to represent.(2590)
*/
[...InorderTraversal<T['left']>, T['val'], ...InorderTraversal<T['right']>]
:
[];
因为T可能是个联合类型,这时使用条件类型判断会发生分布行为,然后又存在递归调用InorderTraversal,每一次调用都会产生分布行为,所以就出问题了:类型处理可能是无穷尽的,还会产生复杂的联合类型。
当时也没明白过来,然后就去解答区看了个👍多的答案:
type InorderTraversal<T extends TreeNode | null, NT extends TreeNode = NonNullable<T>> =
T extends null
?
[]
:
[...InorderTraversal<NT['left']>, NT['val'], ...InorderTraversal<NT['right']>];
由于定义了一个新的类型NT,并且将其类型确定为TreeNode,所以后面直接使用NT来获取对应的属性类型是可行的,不会报错。
在解答区多看了几个答案后,结合一开始我所定义类型中报的错,就明白了,所以不管是哪个答案,主要问题就是确定T的类型,然后才能进行处理使用。
Flip
type Flip<T extends Record<string, any>> = {
[P in keyof T as `${T[P]}`]: P
}
参考自解答区
使用in遍历对象的键时,再通过 as 映射一个新的键,不就解决了吗?所以一开始我是这么干的:
type Flip<T> = {
[P in keyof T as `${T[P]}`]: P
}
/*
所以报错了,在 T[P] 下面会有红色波浪线
报错内容如下:
Type 'T[P]' is not assignable to type 'string | number | bigint | boolean | null | undefined'.
Type 'T[keyof T]' is not assignable to type 'string | number | bigint | boolean | null | undefined'.
Type 'T[string] | T[number] | T[symbol]' is not assignable to type 'string | number | bigint | boolean | null | undefined'.
Type 'T[string]' is not assignable to type 'string | number | bigint | boolean | null | undefined'.(2322)
*/
一般情况下,能作为对象键的类型为string、number、symbol,所以通过T[P]去获取值类型是不对的,就报错了。
所以得将P的类型给确定下来,那么可以通过限制T的类型来解决:
// 使用内置工具类型 Record 来限制 T 的类型,这样键的类型就确定了
type Flip<T extends Record<string, any>> = {
[P in keyof T as `${T[P]}`]: P
}
斐波那契序列
/** 根据数值生成指定长度的元组 */
type GenerateTuple<L extends number, T extends number[] = []> =
T['length'] extends L ? T : GenerateTuple<L, [...T, 0]>;
/** 根据当前数列,计算下一项的值 */
type Calculate<T extends number[]> =
T extends [...any, infer A, infer B]
? [...GenerateTuple<A extends number ? A : never>, ...GenerateTuple<B extends number ? B : never>]['length']
: T['length'];
type Fibonacci<T extends number, A extends number[] = [1]> =
A['length'] extends T
?
A extends [...any, infer E]
? E
: never
: Fibonacci<T, [...A, Calculate<A>]>;
逻辑剖析
Case 如下:
Expect<Equal<Fibonacci<3>, 2>>
1. 代入到类型当中:
type Fibonacci<3, [1]> =
1 extends 3 // 条件不成立
?
A extends [...any, infer E]
? E
: never
// 走到这,结果为 Fibonacci<3, [1, Calculate<A>]>
: Fibonacci<3, [1, Calculate<[1]>]>;
1.1 根据上一步的结果,代入到 Calculate中,计算出数列第 2 个的值:
type Calculate<[1]> =
[1] extends [...any, infer A, infer B] // 条件不成立
? [...GenerateTuple<A extends number ? A : never>, ...GenerateTuple<B extends number ? B : never>]['length']
// 走到这,结果为 1
: T['length'];
// 最终结果为:
Fibonacci<3, [1, 1]>
2. 根据第一步的结果,再次代入到Fibonacci中:
type Fibonacci<3, [1, 1]> =
2 extends 3 // 条件不成立
?
A extends [...any, infer E]
? E
: never
// 走到这,结果为 Fibonacci<3, [1, 1, Calculate<[1, 1]>]>
: Fibonacci<3, [1, 1, Calculate<[1, 1]>]>
2.1 使用Calculate计算第数列中第 3 个值:
type Calculate<[1, 1]> =
[1, 1] extends [...any, infer A, infer B] // 条件成立
?
/*
走到这里,GenerateTuple 就是用来生成一个指定长度的元组
这样子把 A 和 B 加起来,就是下一个值
所以最终结果:[1, 1]['length'] = 2
*/
[
...GenerateTuple<1>,
...GenerateTuple<1>
]['length']
: T['length'];
// 最终结果为:
Fibonacci<3, [1, 1, 2]>
3. 根据第 2 步的最终结果继续:
type Fibonacci<3, [1, 1, 2]> =
3 extends 3 // 条件成立
?
[1, 1, 2] extends [...any, infer E] // 条件成立
// 走到这,最终结果为 2
? 2
: never
: Fibonacci<T, [...A, Calculate<A>]>;
最终结果为 2,符合 case ,测试通过。
我想出来的比较复杂,虽然做出来了,但不妨碍去看看更好更简洁的解答:
type Fibonacci
<
T extends number,
// 记录数列的索引,就是计算到第几个值了
CurrentIndex extends any[] = [1],
// 数列倒数第二个值
Prev extends any[] = [],
// 数列最后面一个值
Current extends any[] = [1]
> =
// 当数列计算的索引与 T 一致时,就可以返回数列最后一个值的长度了
CurrentIndex['length'] extends T
?
Current['length']
:
Fibonacci<T, [...CurrentIndex, 1], Current, [...Prev, ...Current]>