TS类型编程-判断两个数字的大小

1,020 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第21天,点击查看活动详情

前言

JavaScript 中,可以很容易的利用大于号小于号判断出两个数字的大小,但是在 TypeScript 中,每次使用大于号小于号都会报错。

虽然在类型编程中,用到数字对比的情况基本没有,但还是处于好奇尝试了一下,本文记叙了相关思路的实现。

基本思路

  1. 使用要进行对比的两个数字,分别创建相应长度的两个数组;
  2. 使用 keyof,获取数组索引组成联合类型;
  3. 判断这两个由数组索引组成的联合类型的 extends 关系,数字比较小的联合类型必然是数字比较大的联合类型的子类型

代码实现

首先,先定义类型工具,用于判断第一个数字是否大于第二个数字:

type MoreThanNumber = any

接着就是确定泛型,因为是比较两个数字之间的大小,所以必然接受两个泛型,代表参与对比的两个数字:

type MoreThanNumber<NumberA extends number, NumberB extends number> = any

然后在判断之前,需要先确定传入的两个泛型是 number 的字面量类型,虽然约束了泛型 extends number,但是还有 nevernumber 本身也是符合约束的,可这两个不能参与对比,需要先排除掉:

type IsNever<T> = [T] extends [never] ? true : false

type IsTypeSelf<TA, TB> = TA extends TB ? TB extends TA ? true : false : false

type IslegalNumber<N> = IsNever<N> extends true ? false : IsTypeSelf<number, N> extends true ? false : true

type MoreThanNumber<NumberA extends number, NumberB extends number> = 
  IslegalNumber<NumberA> extends false // 判断 NumberA 是否是一个合法 number 字面量类型
  ? false // 不是就返回 false
  : IslegalNumber<NumberB> extends false // 如果 NumberA 是,再判断 NumberB 是不是合法
  ? false // NumberB 不是就返回 false
  : any

而这个工具类型的功能就是判断第一个数字是否小于第二个数字,按照上面的思路,先将数字转换为对应长度的数组,然后用 keyof 获取数组索引的联合类型,判断 B extends A 是否成立,如果成立,说明 BA 的子类型:

type MoreThanNumber<NumberA extends number, NumberB extends number> = 
  IslegalNumber<NumberA> extends false
  ? false
  : IslegalNumber<NumberB> extends false
  ? false
  : keyof FixedArray<string, NumberB> extends keyof FixedArray<string, NumberA>  // 分别创建数组和使用 keyof
  ? true
  : false

FixedArray 能将数字转换为对应长度的数组,详见TS-编写自动创建长度固定数组的类型工具

但是有一个问题,当 BA 完全相同时,B extends A 也是成立,所以还要在判断一遍 A extends B 的结果才行,如果成立,说明 AB相同,如果不成立,才能说明 NumberA 大于 NumberB,最终代码如下:

type MoreThanNumber<NumberA extends number, NumberB extends number> = 
  IslegalNumber<NumberA> extends false
  ? false
  : IslegalNumber<NumberB> extends false
  ? false
  : keyof FixedArray<string, NumberB> extends keyof FixedArray<string, NumberA>  // 判断大于是否成立
  ? keyof FixedArray<string, NumberA> extends keyof FixedArray<string, NumberB> // 判断两者是否校相同
  ? false
  : true // 当 B extends A 成立 且 A extends B 不成立,才返回 true,其他都返回 false
  : false

测试数据如下:

微信截图_20220821184038.png

微信截图_20220821184027.png

当第一个数字小于或者等于第二个数字时,返回 false,只有当第一个数字大于第二个数字时,才会返回 true,如下:

微信截图_20220821184048.png