TypeScript 类型体操解析 (续)

ziyang 10月前 ⋅ 55 阅读
ad

前言

上一篇我们只实现了简单的数值比较,并留下了一个问题;实现大数字的比较。

前置知识

ts类型实例化过程太深,会导致报错"Type instantiation is excessively deep and possibly infinite"

type MarkArr<N extends number, R extends unknown[] = []> = R['length'] extends N
? R
: MarkArr<N, [0, ...R]>;

type Result = MarkArr<999>; // 没有报错
type Result2 = MarkArr<1000>; // error  Type instantiation is excessively deep and possibly infinite

思路

两个数字比较,有如下几种情况(上一篇已经考虑负数的问题了,这里就不再赘述):

1. 只有一位数字,可以直接比较
2. 有多位数字,但是长度不想等,可以直接判断长度来比较大小
3. 有多位数字,且长度相等,可以都从第一位开始判断,大于则是A大于B

多位数字的对比,可以先把数字转换成数组,思路还是字符串infer

 type Number2Array<
   T extends string | number,
   A extends string[] = [],
 > = `${T}` extends `${infer S}${infer O}` ? Number2Array<O, [...A, S]> : A;

有了数字的数组之后,就可以开始进行对比了。可以用一个指针每次对比,如果相同,则指针+1,不相同测返回,这样就可以对整个数组进行对比了。

  type ComparisonNumberArr<
  S extends string[],
  E extends string[],
  P extends number = 0,
> =
// 长度是否相同
  S['length'] extends E['length']
  // 长度相同,则开始从P(默认是第一位0)位开始计算
  ? S[P] extends E[P]
  // 如果相同,则P指针加1,继续计算
    ? ComparisonNumberArr<S, E, Plus<P, 1> & number>
    // 计算
    : CompareNumber<S[P], E[P]>
      // 长度不相同,则对比长度就可以知道大小了
  : CompareNumber<S['length'], E['length']>;

观察上面的代码,都是10以内的数字比较。我们可以生成一个table,预计算(Early Table Generation)数字对比的结果,而不是运行时计算。

// 0-9
type num = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
type NumberMap = {
  [Key in `${num[number]}${num[number]}`]: Key extends `${infer A}${infer B}`
    ? A extends B
      ? Comparison.Equal
      : CompareNumber<String2Number<A>, String2Number<B>>
    : never;
}; 

ComparisonNumberArr可以改为:

type ComparisonNumberArr<
  S extends string[],
  E extends string[],
  P extends number = 0,
> = S['length'] extends E['length']
  ? S[P] extends E[P]
    ? ComparisonNumberArr<S, E, Plus<P, 1> & number>
    // 类型收窄
    : `${S[P]}${E[P]}` extends keyof NumberMap
      ? NumberMap[`${S[P]}${E[P]}`]
      : never
  : CompareNumber<S['length'], E['length']>;

完整的代码和测试用例实现如下

type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2
  ? true
  : false;

type Expect<T extends true> = T;

enum Comparison {
  Greater,
  Equal,
  Lower,
}

type num = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

type MarkArr<N extends number, R extends unknown[] = []> = R['length'] extends N ? R : MarkArr<N, [0, ...R]>;
type Plus<A extends number, B extends number> = [...MarkArr<A>, ...MarkArr<B>]['length'];

type String2Number<S extends string, T extends any[] = []> = `${S}` extends `${T['length']}`
  ? T['length']
  : String2Number<S, [0, ...T]>;
  // 对比两个数字大小
type CompareNumber<A extends number, B extends number> = keyof MarkArr<A> extends keyof MarkArr<B>
  ? Comparison.Lower
  : Comparison.Greater;

// 生成对照表
type NumberMap = {
  [Key in `${num[number]}${num[number]}`]: Key extends `${infer A}${infer B}`
    ? CompareNumber<String2Number<A>, String2Number<B>>
    : never;
};

// 对比长度
  type ComparisonNumberArr<
  S extends string[],
  E extends string[],
  P extends number = 0,
> =
// 长度是否相同
  S['length'] extends E['length']
  // 长度相同,则开始从P(默认是第一位0)位开始计算
  ? S[P] extends E[P]
  // 如果相同,则P指针加1,继续计算
    ? ComparisonNumberArr<S, E, Plus<P, 1> & number>
    // 类型收窄
    : `${S[P]}${E[P]}` extends keyof NumberMap
      ? NumberMap[`${S[P]}${E[P]}`]
      : never
      // 长度不相同,则对比长度就可以知道大小了
  : CompareNumber<S['length'], E['length']>;

// 数字转为字符串的数组
type TowNumberArray<
  T extends string | number,
  A extends string[] = [],
> = `${T}` extends `${infer S}${infer O}` ? TowNumberArray<O, [...A, S]> : A;

type Comparator<M extends number, N extends number> =
// 相等
 M extends N
  ? Comparison.Equal
  // 都是负数,则反转计算的结果
  : `${M}${N}` extends `-${infer M1}-${infer N1}`
    ? ComparisonNumberArr<TowNumberArray<M1>, TowNumberArray<N1>> extends Comparison.Lower
      ? Comparison.Greater
      : Comparison.Lower
      // 第一个为负数
    : `${M}${N}` extends `-${infer M1}${infer N1}`
      ? Comparison.Lower
      // 第二个为负数
      : `${M}${N}` extends `${infer M1}-${infer N1}`
        ? Comparison.Greater
        //  都是正数
        : `${M}${N}` extends `${infer M2}${infer N2}`
          ? ComparisonNumberArr<TowNumberArray<M>, TowNumberArray<N>>
          : never;

type cases = [
  Expect<Equal<Comparator<5, 5>, Comparison.Equal>>,
  Expect<Equal<Comparator<5, 6>, Comparison.Lower>>,
  Expect<Equal<Comparator<5, 8>, Comparison.Lower>>,
  Expect<Equal<Comparator<5, 0>, Comparison.Greater>>,
  Expect<Equal<Comparator<-5, 0>, Comparison.Lower>>,
  Expect<Equal<Comparator<0, 0>, Comparison.Equal>>,
  Expect<Equal<Comparator<0, -5>, Comparison.Greater>>,
  Expect<Equal<Comparator<5, -3>, Comparison.Greater>>,
  Expect<Equal<Comparator<5, -7>, Comparison.Greater>>,
  Expect<Equal<Comparator<-5, -7>, Comparison.Greater>>,
  Expect<Equal<Comparator<-5, -3>, Comparison.Lower>>,
  Expect<Equal<Comparator<-25, -30>, Comparison.Greater>>,
  Expect<Equal<Comparator<15, -23>, Comparison.Greater>>,
  Expect<Equal<Comparator<40, 37>, Comparison.Greater>>,
  Expect<Equal<Comparator<-36, 36>, Comparison.Lower>>,
  Expect<Equal<Comparator<27, 27>, Comparison.Equal>>,
  Expect<Equal<Comparator<-38, -38>, Comparison.Equal>>,

  Expect<Equal<Comparator<1, 100>, Comparison.Lower>>,
  Expect<Equal<Comparator<100, 1>, Comparison.Greater>>,
  Expect<Equal<Comparator<-100, 1>, Comparison.Lower>>,
  Expect<Equal<Comparator<1, -100>, Comparison.Greater>>,
  Expect<Equal<Comparator<-100, -1>, Comparison.Lower>>,
  Expect<Equal<Comparator<-1, -100>, Comparison.Greater>>,

  Expect<Equal<Comparator<9007199254740992, 9007199254740992>, Comparison.Equal>>,
  Expect<Equal<Comparator<-9007199254740992, -9007199254740992>, Comparison.Equal>>,
  Expect<Equal<Comparator<9007199254740991, 9007199254740992>, Comparison.Lower>>,
  Expect<Equal<Comparator<9007199254740992, 9007199254740991>, Comparison.Greater>>,
  Expect<Equal<Comparator<-9007199254740992, -9007199254740991>, Comparison.Lower>>,
  Expect<Equal<Comparator<-9007199254740991, -9007199254740992>, Comparison.Greater>>,
];

完结撒花

关于纵目

江苏纵目信息科技有限公司是一家专注于运维监控软件产品研发与销售的高科技企业。覆盖全链路应用性能监控、IT基础设施监控、物联网数据采集数据观测等场景,基于Skywalking、Zabbix、ThingsBoard等开源体系构建了ArgusAPM、ArgusOMS、ZeusIoT等产品,致力于帮助各行业客户构建集聚可观测性的统一运维平台、物联网大数据平台。

  点赞 0   收藏 0
  • ziyang
    共发布1篇文章 获得0个收藏
全部评论: 0