Libertus Chen-U
  1. 1 Libertus Chen-U
  2. 2 Last Surprise Lyn
  3. 3 Warcry mpi
  4. 4 Flower Of Life 发热巫女
  5. 5 Life Will Change Lyn
  6. 6 Hypocrite Nush
  7. 7 Time Bomb Veela
  8. 8 One Last You Jen Bird
  9. 9 かかってこいよ NakamuraEmi
  10. 10 The Night We Stood Lyn
  11. 11 Quiet Storm Lyn
2022-10-06 21:22:16

类型体操基本功-简单四则运算

很多类型推导的场景下需要涉及到数字的计算,这篇文章就介绍下常规四则运算的实现思路。

加法

实现

首先应该想到,在Typescript中得到一个数字结果的方法最简单的就是获取数组的长度

type Length<T extends any[]> = T["length"]

加法沿着这个思路就很好解决:将两个数字转化为对应长度的数组,再合并计算长度即可。

// 数字转数组
type ConstructTuple<L extends number, Res extends unknown[] = []> = Res['length'] extends L
    ? Res 
    : ConstructTuple<L, [...Res, unknown]>

// 合并计算长度
type Add<A extends number, B extends number> = [...ConstructTuple<A>, ...ConstructTuple<B>]['length']

至此我们就实现了Add基础泛型。结合一个实例看一下使用。

应用

实现一个获取两个数字之间范围的泛型。

type result = NumberRange<2 , 9> //  | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 

已知左右边界,设置一个当前值和结果值,从左边界开始递归加1,到达右边界返回结果值即可。

type NumberRange<L extends number, R extends number, Cur extends number = L, Res = L | R> = Cur extends R
    ? Res
    : NumberRange<L, R, Add<Cur, 1>, Res | Cur>

许多工具泛型的实现都离不开四则运算的基础泛型。接下来看下减法基础泛型的实现。

减法

减法的实现不能像加法一样做简单的合并,不过思路差别也不大,都是利用数组长度去计算。同样将两个数字转成数组,每次移除更长数组的一个元素,并将结果加1,当两个数组长度相等时,记录的结果就是减法结果。

在此之前先实现数组的推出操作。

type Shift<T extends any[]> = T extends [infer A, ...infer B] ? B : []

再基于减法思路实现数组的减法计算。

type SubList<A extends unknown[], B extends unknown[], Res extends unknown[] = []> = A['length'] extends B['length'] 
    ? Res['length']
    : SubList<Shift<A>, B, [...Res, unknown]>

最后封装一层数字转数组就完成了。

type Sub<A extends number, B extends number> = SubList<ConstructTuple<A>, ConstructTuple<B>>

还有一种更简单的方法,利用Typescript的数组解构和infer特性直接推断长度。

type Sub2<A extends number, B extends number> = ConstructTuple<A> extends [...ConstructTuple<B>, ...infer Res]
    ? Res['length']
    : never

乘法

乘法本质上是加法的累加,比如2 * 3即可等效于2 + 2 + 2。基于加法实现,添加一个计数器,以左侧数字为加法基础,右侧数字为计数器即可。

type Muti<A extends number, B extends number, Res extends unknown[] = []> = B extends 0
    ? Res['length']
    : Muti<A, Sub<B, 1>, [...Res, ...ConstructTuple<A>]>

除法

除法同乘法, 3 / 2等效于 3 - 2 - 2.....的循环,直到剩余数小于除数为止。

type Div<A extends number, B extends number, Res extends unknown[] = []>
 = Sub<A, B> extends never
    ? Res['length']
    : Div<Sub<A, B>, B, [...Res, unknown]>

小结

当然以上实现的四则运算还有很多缺陷,比如

  • 不支持负数和小数
  • 对运算顺序有要求
  • 除法是向上取整的

不过在大多数更高阶的类型设计中,并不会真正进行单纯复杂的以计算结果为目的的四则运算,更多的是作为记数器的逻辑需要去进行基础计算,所以简单四则也基本能覆盖需求场景。如果对更复杂的四则感兴趣,也可以思考下如何实现。

-- EOF --

添加在分类「 前端开发 」下,并被添加 「TypeScript」 标签。