TypeScript 杂记三 《Slice》
Posted 左手121
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了TypeScript 杂记三 《Slice》相关的知识,希望对你有一定的参考价值。
TypeScript 杂记三 《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]
,因此直接返回空数组
- H 辅助我们计算,在 H 长度不等于 Start 情况下,给 H 加入一条数据(
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]
以上是关于TypeScript 杂记三 《Slice》的主要内容,如果未能解决你的问题,请参考以下文章