# 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]>;

暂时还是以递归方式解决,其他方案是真体操,没有太多实际意义

# 知识点

  1. ts 中通过构建元组,并根据其长度来实现运算
  2. ts 中最大递归为 1000
Last Updated: 2023/5/16 06:00:28