TS类型技巧(四):计数

通过构建元组做计数

// 计算的基础
type BuildArray<
    Length extends number,
    Ele = unknown,
    Arr extends unknown[] = []
> = Arr['length'] extends Length
    ? Arr
    : BuildArray<Length, Ele, [...Arr, Ele]>;

1. 加法

type Add<Num1 extends number, Num2 extends number> =
    [...BuildArray<Num1>, ...BuildArray<Num2>]['length'];

2. 减法

infer Rest提取 本质上就是减法

type Subtract<Num1 extends number, Num2 extends number> =
    BuildArray<Num1> extends [...arr1: BuildArray<Num2>, ...arr2: infer Rest]
    ? Rest['length']
    : never;

arr1:arr2: tuple label(标签)语法,提高可读性, 和不加标签一样

arr1:arr2 并不能作为变量供后续使用,仅 infer Rest 可供后续使用

[...BuildArray<Num2>, ...infer Rest] 完全等同

TS 4.1–4.9 必须整个元组都带标签,或整个都不带,上半arr1,而下半没有标签

3. 乘法

type Multiply<
    N1 extends number,
    N2 extends number,
    R extends unknown[] = []
> = N2 extends 0 ? R['length']
    : Multiply<N1, Subtract<N2, 1>, [...R, ...BuildArray<N1>]>;

Result 每次加N1, N2每次-1, 直到N2为0, 返回Result

4. 除法

type Divide<
    N1 extends number,
    N2 extends number,
    R extends unknown[] = []
> = N1 extends 0 ? R['length']
        : Divide<Subtract<N1, N2>, N2, [...R, unknown]>;

5. 字符串长度

字符串类型不能取 length

type StrLen<Str extends string, R extends unknown[] = []> =
    Str extends `${string}${infer Rest}`
    ? StrLen<Rest, [...R, unknown]>
    : R['length']

6. 数值比较

往一个数组类型中不断放入元素取长度,如果先到了 A,那就是 B 大,否则是 A 大

type GreaterThan<
    N1 extends number,
    N2 extends number,
    R extends unknown[] = []
> = N1 extends N2 
    ? false
    : R['length'] extends N2
        ? true
        : R['length'] extends N1
            ? false
            : GreaterThan<N1, N2, [...R, unknown]>;

7. Fibonacci

type FibonacciLoop<
    // 之前累加的数值
    PrevArr extends unknown[], 
    // 当前数值
    CurrentArr extends unknown[], 
    // index
    IndexArr extends unknown[] = [], 
    // 数列的第几个数
    Num extends number = 1
> = IndexArr['length'] extends Num
    ? CurrentArr['length']
    : FibonacciLoop<CurrentArr, [...PrevArr, ...CurrentArr], [...IndexArr, unknown], Num> 

// 构建斐波那契数列,真正要传入的是Num
type Fibonacci<Num extends number> = FibonacciLoop<[1], [], [], Num>;