ts中的数值计算

463 阅读4分钟

TypeScript 类型系统中没有加减乘除运算符,但是可以通过构造不同的数组然后取 length 的方式来完成数值计算

把数值的加减乘除转化为对数组的提取和构造。

1、数组长度实现加减乘除

1、构造长度为Length的Ele类型的数组

type BuildArray34<
  Length extends number,
  Ele = unknown,
  Arr extends unknown[] = []
> = Arr['length'] extends Length
  ? Arr
  : BuildArray34<Length, Ele, [...Arr, Ele]>;
  
type arr34_1 =BuildArray34<3,number>;
//type arr34_1 = [number, number, number]

2、ADD

通过构造一定长度的数组取 length 的方式实现了加法运算。

//比如 3 + 2,
//就是构造一个长度为 3 的数组类型,再构造一个长度为 2 的数组类型
//然后合并成一个数组,取 length。

type Add34<Num1 extends number, Num2 extends number> = 
    [...BuildArray34<Num1>,...BuildArray34<Num2>]['length'];
    
type testAdd=Add34<20,31>;
//type testAdd = 51

3、Subtract

可以通过数组类型的提取来做。

比如 3-1:

3 是 [unknown, unknown, unknown] 的数组类型

提取出 2 个元素之后 剩下的数组再取 length 就是 1。

type Subtract34<Num1 extends number, Num2 extends number> =
  BuildArray34<Num1> extends [...arr1: BuildArray34<Num2>, ...arr2: infer Rest]
  ? Rest['length']
  : never;
//类型参数 Num1、Num2 分别是被减数和减数,通过 extends 约束为 number。
//构造 Num1 长度的数组,通过模式匹配提取出 Num2 长度个元素,剩下的放到 infer 声明的局部变量 Rest 里。
//取 Rest 的长度返回,就是减法的结果。

type testSubtract34=Subtract34<32,12>;
//type testSubtract34 = 20

4、Multiply

1 乘以 5 就相当于 1 + 1 + 1 + 1 + 1,也就是说乘法就是多个加法结果的累加。

那么我们在加法的基础上,多加一个参数来传递中间结果的数组,算完之后再取一次 length 就能实现乘法

type Mutiply34<
  Num1 extends number,
  Num2 extends number,
  ResultArr extends unknown[] = []
> = Num2 extends 0 ? ResultArr['length']
  : Mutiply34<Num1, Subtract34<Num2, 1>, [...BuildArray34<Num1>, ...ResultArr]>;
//每加一次就把 Num2 减一,直到 Num2 为 0,就代表加完了。
//加的过程就是往 ResultArr 数组中放 Num1 个元素。
//这样递归的进行累加,也就是递归的往 ResultArr 中放元素。
//最后取 ResultArr 的 length 就是乘法的结果。

type testMutiply34=Mutiply34<5,3>;
//type testMutiply34 = 15

5、Divide

除法的实现就是被减数不断减去减数,直到减为 0,记录减了几次就是结果。

type Divide34<
  Num1 extends number,
  Num2 extends number,
  CountArr extends unknown[] = []
> = Num1 extends 0 ? CountArr['length']
  : Divide34<Subtract34<Num1, Num2>, Num2, [unknown, ...CountArr]>;
//类型参数 Num1 和 Num2 分别是被减数和减数。
//类型参数 CountArr 是用来记录减了几次的累加数组。
//如果 Num1 减到了 0 ,那么这时候减了几次就是除法结果,也就是 CountArr['length']。
//否则继续递归的减,让 Num1 减去 Num2
//并且 CountArr 多加一个元素代表又减了一次。

type testDivide34=Divide34<30,6>;
//type testDivide34 = 5

2、数组长度实现计数

1、求字符串长度

字符串长度不确定,明显要用递归。每次取一个并计数,直到取完,就是字符串长度

type StrLen35<
  Str extends string,
  CountArr extends unknown[] = []
> = Str extends `${string}${infer Rest}`
  ? StrLen35<Rest, [...CountArr, unknown]>
  : CountArr['length']
//通过模式匹配提取去掉一个字符之后的剩余字符串,并且往计数数组里多放入一个元素。递归进行取字符和计数。
//如果模式匹配不满足,代表计数结束,返回计数数组的长度 CountArr['length']。

type testStrLen35=StrLen35<'hello my name is lily'>;
//type testStrLen35 = 21

2、做两个数值的比较

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

type GreaterThan35<
  Num1 extends number,
  Num2 extends number,
  CountArr extends unknown[] = []
> = Num1 extends Num2
  ? false
  : CountArr['length'] extends Num2
  ? true
  : CountArr['length'] extends Num1
  ? false
  : GreaterThan35<Num1, Num2, [...CountArr, unknown]>;
//类型参数 Num1 和 Num2 是待比较的两个数。
//类型参数 CountArr 是计数用的,会不断累加,默认值是 [] 代表从 0 开始。
//如果 Num1 extends Num2 成立,代表相等,直接返回 false。
//否则判断计数数组的长度,如果先到了 Num2,那么就是 Num1 大,返回 true。
//反之,如果先到了 Num1,那么就是 Num2 大,返回 false。
//如果都没到就往计数数组 CountArr 中放入一个元素,继续递归。

type testGreaterThan35 = GreaterThan35<5, 6>;
//type testGreaterThan35 = false

3、Fibonacci 数列的计算

F(0) = 1,F(1) = 1, F(n) = F(n - 1) + F(n - 2)(n ≥ 2,n ∈ N*)

type FibonacciLoop35<
  PrevArr extends unknown[],
  CurrentArr extends unknown[],
  IndexArr extends unknown[] = [],
  Num extends number = 1
> = IndexArr['length'] extends Num
  ? CurrentArr['length']
  : FibonacciLoop35<CurrentArr, [...PrevArr, ...CurrentArr], [...IndexArr, unknown], Num>
//类型参数 PrevArr 是代表之前的累加值的数组
//类型参数 CurrentArr 是代表当前数值的数组。
//类型参数 IndexArr 用于记录 index,每次递归加一,默认值是 [],代表从 0 开始。
//类型参数 Num 代表求数列的第几个数。
//判断当前 index 也就是 IndexArr['length'] 是否到了 Num,到了就返回当前的数值 CurrentArr['length']。
//否则求出当前 index 对应的数值,用之前的数加上当前的数 [...PrevArr, ... CurrentArr]。
//然后继续递归,index + 1,也就是 [...IndexArr, unknown]。
type Fibonacci<Num extends number> = FibonacciLoop35<[1], [], [], Num>;

type testFibonacci = Fibonacci<8>;
//type testFibonacci = 21