# 2257-减一
# 题目描述
给定一个正整数作为类型的参数,要求返回的类型是该数字减 1。
例如:
type Zero = MinusOne<1>; // 0
type FiftyFour = MinusOne<55>; // 54
# 分析
第一次看到这个题目的时候,内心必然是:wc,这是啥?看完答案:还能这样,高端高端?
ts 虽然是图灵完备的,但是自身没有提供加减乘除这样的运算符。想要实现类似的操作,只能借助元组的长度进行计数。
type ArrWithLength<Length extends number, Arr extends any[] = []> =
// 元组长度等于目标长度时
Arr['length'] extends Length
? // 返回元组
Arr
: // 否则,向 Arr 中增加一个元素,并递归处理新数组
ArrWithLength<Length, [...Arr, any]>;
// Arr3 = [any, any, any]
type Arr3 = ArrWithLength<3>;
// Arr5 = [any, any, any, any, any]
type Arr5 = ArrWithLength<5>;
通过递归的方式,就可以构建出一个长度为任意值的元组,借助其 length 属性,就可以达到计数的效果。
那么减 1,其实就可以先构造长度为 length 的元组,然后使用 实现 Pop 中移除一个元素后,其 length 就是要求的结果。
type Pop<T extends any[]> = T extends [...infer F, infer R] ? F : [];
type ArrWithLength<Length extends number, Arr extends any[] = []> =
// 元组长度等于目标长度时
Arr['length'] extends Length
? // 返回元组
Arr
: // 否则,向 Arr 中增加一个元素,并递归处理新数组
ArrWithLength<Length, [...Arr, any]>;
type MinusOne<T extends number> = Pop<ArrWithLength<T>>['length'];
如此,是一种思路,当然,不借助 Pop 和 ArrWithLength 直接递归实现也是可以的:
type MinusOne<T extends number, Arr extends any[] = []> =
// 如果 Arr 加上一个元素的长度等于目标长度
[...Arr, 1]['length'] extends T
? // 那么此时 Arr 的长度就是要求的减一
Arr['length']
: // 否则继续增加 Arr 长度
MinusOne<T, [...Arr, 1]>;
但是,ts 的递归深度有 1000 的限制,此时,就会出现 Type instantiation is excessively deep and possibly infinite.
这样的警告,同时也无法计算出实际的类型。
而题目中,也有超过 1000 的限制的用例,那么此时还有什么办法解决吗?一刷的话,其实不建议继续研究了,等二刷三刷的时候再研究也不迟。TODO:
# 题解
type MinusOne<T extends number, Arr extends any[] = []> = [
...Arr,
1,
]['length'] extends T
? Arr['length']
: MinusOne<T, [...Arr, 1]>;
暂时还是以递归方式解决,其他方案是真体操,没有太多实际意义
# 知识点
- ts 中通过构建元组,并根据其长度来实现运算
- ts 中最大递归为 1000
← 休息下 2595-实现PickByType →