携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第21天,点击查看活动详情
前言
在 JavaScript 中,可以很容易的利用大于号和小于号判断出两个数字的大小,但是在 TypeScript 中,每次使用大于号和小于号都会报错。
虽然在类型编程中,用到数字对比的情况基本没有,但还是处于好奇尝试了一下,本文记叙了相关思路的实现。
基本思路
- 使用要进行对比的两个数字,分别创建相应长度的两个数组;
- 使用
keyof,获取数组索引组成联合类型; - 判断这两个由数组索引组成的联合类型的
extends关系,数字比较小的联合类型必然是数字比较大的联合类型的子类型。
代码实现
首先,先定义类型工具,用于判断第一个数字是否大于第二个数字:
type MoreThanNumber = any
接着就是确定泛型,因为是比较两个数字之间的大小,所以必然接受两个泛型,代表参与对比的两个数字:
type MoreThanNumber<NumberA extends number, NumberB extends number> = any
然后在判断之前,需要先确定传入的两个泛型是 number 的字面量类型,虽然约束了泛型 extends number,但是还有 never 和 number 本身也是符合约束的,可这两个不能参与对比,需要先排除掉:
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 是否成立,如果成立,说明 B 是 A 的子类型:
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-编写自动创建长度固定数组的类型工具。
但是有一个问题,当 B 与 A 完全相同时,B extends A 也是成立,所以还要在判断一遍 A extends B 的结果才行,如果成立,说明 A、B相同,如果不成立,才能说明 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
测试数据如下:
当第一个数字小于或者等于第二个数字时,返回 false,只有当第一个数字大于第二个数字时,才会返回 true,如下: