TypeScript 杂记三 《Slice》

这篇博客详细介绍了如何利用TypeScript的类型系统实现数组的slice功能,包括处理负数索引、计算超出数组长度的边界情况,并通过多个辅助函数逐步构建解决方案。内容涵盖了负数索引的转换、Start和End参数的处理,以及在遇到超出数组长度的索引时的错误检查。博客还提供了完整的代码实现和测试用例。
摘要由CSDN通过智能技术生成

TypeScript 杂记三 《Slice》

Slice

实现 Slice 的功能

// Slice<Arr, Start, End>
type Arr = [1, 2, 3, 4, 5];
type Result = Slice<Arr, 2, 4>; // expected to be [3, 4]
  • 大致的定义如下,Start 和 End 可以省略,省略的时候一个默认是 0,一个默认是数组的长度
type Slice<
  A extends any[],
  S extends number = 0,
  N extends number = A["length"]
> = {};
  • 考虑 Start 和 End 可能是负数,因此我们需要一个辅助函数去处理负数
// 如果 N 是负数,取出对应的数字去计算
// 如果是整数就直接返回
type Index<A extends any[], N extends number> = `${N}` extends `-${infer R}`
  ? Minus<A, R>
  : N;
  • 计算的思路
    • R["length"] 就是我们返回的对应的数字
    • H["length"] 就是我们获取到的正数
    • 一开始 H R 都是空
    • 判断 H["length"] === N ,假 则给 H 数组补充一个值,直到两个相等
    • 判断 H + R 的长度是否等于原本数组的长度,如果不想等就给 R 补充一个值,直到 H + R === A
    • 细心一点就会发现这样,如果我输入的是一个超出限制的负数数字,那么就会死循环。(只考虑负数超出限制,先不考虑正数超出限制,下一步的操作会处理正数的问题。)其实 TS 已经为我们做出了判断,如果数字超出限制会报错,如下:
type Minus<
  A extends any[],
  N extends string,
  H extends unknown[] = [],
  R extends unknown[] = []
> = `${H["length"]}` extends N
  ? [...H, ...R]["length"] extends A["length"]
    ? R["length"]
    : Minus<A, N, H, [...R, unknown]>
  : Minus<A, N, [...H, unknown], R>;

type Arr = [1, 2, 3, 4, 5];

type Test1 = Index<Arr, -1>; // 4
type Test2 = Index<Arr, -2>; // 3
type Test3 = Index<Arr, -4>; // 1
type Test4 = Index<Arr, -5>; // 0
type Test5 = Index<Arr, -6>; // 报错:类型实例化过深,且可能无限。ts(2589)
  • 我们通过一个辅助函数去计算最终的结果,大概就是如下的样子
type Slice<
  A extends any[],
  S extends number = 0,
  N extends number = A["length"]
> = Helper<A, Index<A, S>, Index<A, N>>;
  • 我们先实现仅仅指定 Start 的情况
    • H 辅助我们计算,在 H 长度不等于 Start 情况下,给 H 加入一条数据([...H, F])。A 减去一条数据(L)(递归调用)
    • 当相等的时候,返回 A
    • 细心一点就会发现,超出限制的数字返回的就是空数组,这是因为当数字很大的时候,最后一次 A = [],这个时候就不满足 A extends [infer F, ...infer L],因此直接返回空数组
type Helper<
  A extends any[],
  S extends number = 0,
  H extends any[] = []
> = A extends [infer F, ...infer L]
  ? H["length"] extends S
    ? A
    : Helper<L, S, [...H, F]>
  : [];

type Test1 = Helper<Arr, 1>; // [2, 3, 4, 5]
type Test2 = Helper<Arr>; // [1, 2, 3, 4, 5]
type Test3 = Helper<Arr, 4>; // [5]
type Test4 = Helper<Arr, 5>; // []
type Test5 = Helper<Arr, 6>; // []
  • 我们再实现指定 End 的情况,额外定义一个辅助函数去专门计算 End 的情况。大概表现如下:
type Helper<
  A extends any[],
  S extends number = 0,
  // 加入 End
  N extends number = A["length"],
  H extends any[] = []
> = A extends [infer F, ...infer L]
  ? H["length"] extends S
    ? // 这里改一下
      HelperEnd<A, N, H>
    : Helper<L, S, N, [...H, F]>
  : [];
  • 实现 HelperEnd
    • 首先 Start 计算完之后,此时才开始计算对应的实际的返回值,因此我们将最新的 A 传递进去,N 是 end 下标,因为 N 是结束的下标,所以我们需要当前的 H 来继续计算
    • R 是另外一个辅助数组,用来存储实际返回的数据
    • 原理和计算 Start 一样,不同的地方是需要将对应的数据存储到 R,且这个 R 是具体的返回值([...R, F]
    • 超出限制的情况参考上述 Start
type HelperEnd<
  A extends any[] = [],
  N extends number = A["length"],
  H extends any[] = [],
  R extends any[] = []
> = A extends [infer F, ...infer L]
  ? H["length"] extends N
    ? R
    : HelperEnd<L, N, [...H, F], [...R, F]>
  : R;
  • 额外考虑一下,如果 Start 大于 End 那么直接返回空数组,大概含义如下:
    • 如果 H 的长度等于 End,那么 Start 要么和我一样,要么比我大,直接返回 false
    • 如果 H 的长度不等于 End,且长度等于 Start,此时直接返回 true
    • 两个都不想等,就给 H 添加一个值
    • 简单点就是,H 不等于任何值就加 1,如果 H 的值先等于了 Start 就是 true,否则就是 false
type Compare<
  S extends number,
  N extends number,
  H extends unknown[] = []
> = H["length"] extends N
  ? false
  : H["length"] extends S
  ? true
  : Compare<S, N, [...H, unknown]>;

type Test1 = Compare<11, 10>; // false
type Test1 = Compare<10, 10>; // false
type Test1 = Compare<0, 10>; // true

完整结果

type Minus<
  A extends any[],
  N extends string,
  H extends unknown[] = [],
  R extends unknown[] = []
> = `${H["length"]}` extends N
  ? [...H, ...R]["length"] extends A["length"]
    ? R["length"]
    : Minus<A, N, H, [...R, unknown]>
  : Minus<A, N, [...H, unknown], R>;

type Index<A extends any[], N extends number> = `${N}` extends `-${infer R}`
  ? Minus<A, R>
  : N;

type HelperEnd<
  A extends any[] = [],
  N extends number = A["length"],
  H extends any[] = [],
  R extends any[] = []
> = A extends [infer F, ...infer L]
  ? H["length"] extends N
    ? R
    : HelperEnd<L, N, [...H, F], [...R, F]>
  : R;

type Compare<
  S extends number,
  N extends number,
  H extends unknown[] = []
> = H["length"] extends N
  ? false
  : H["length"] extends S
  ? true
  : Compare<S, N, [...H, unknown]>;

type Helper<
  A extends any[],
  S extends number = 0,
  N extends number = A["length"],
  H extends any[] = []
> = Compare<S, N> extends true
  ? A extends [infer F, ...infer L]
    ? H["length"] extends S
      ? HelperEnd<A, N, H>
      : Helper<L, S, N, [...H, F]>
    : []
  : [];

type Slice<
  A extends any[],
  S extends number = 0,
  E extends number = A["length"]
> = Helper<A, Index<A, S>, Index<A, N>>;

type Arr = [1, 2, 3, 4, 5];
type Result = Slice<Arr, 2, 4>; // [3, 4]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值