Extreme 难度的 Slice 过关了,终于可以自称类型体操入门了(喜)
题面 ¶
type-challenges/questions/00216-extreme-slice/README.md at main · type-challenges/type-challenges
Slice
¶
by Anthony Fu @antfu
Implement the JavaScript Array.slice
function in the type system. Slice<Arr, Start, End>
takes the three argument. The output should be a subarray of Arr
from index Start
to End
. Indexes with negative numbers should be counted from reversely.
For example
type Arr = [1, 2, 3, 4, 5];
type Result = Slice<Arr, 2, 4>; // expected to be [3, 4]
思路 ¶
看上去很简单?这真的是 Extreme 难度吗?
难度主要在数字的转换上。TypeScript 不支持直接做类型的加减法,所以需要体操一下获得 Add1<N>
和 MinusXY<X, Y>
type Push1<X extends any[]> = [...X, 1];
type ArrFrom<N, A extends any[] = []> = A["length"] extends N
? A
: ArrFrom<N, Push1<A>>;
type Add1<N> = [...ArrFrom<N>, 1]["length"];
type Minus1<N> = ArrFrom<N> extends [infer _1, ...infer Rest]
? Rest["length"]
: never;
type MinusXY<X, Y> = Y extends 0 ? X : MinusXY<Minus1<X>, Minus1<Y>>;
type Abs<X extends number> = `${X}` extends `-${infer R extends number}`
? R
: X;
type IsNeg<X extends number> = Abs<X> extends X ? false : true;
剩下的就很简单了
type RemoveFirstN<N, Arr extends any[], Removed = 0> = N extends Removed
? Arr
: Arr extends [infer _1, ...infer Rest extends any[]]
? RemoveFirstN<N, Rest, Add1<Removed>>
: [];
type KeepFirstN<
N,
Arr extends any[],
Ret extends any[] = [],
Kept = 0
> = Kept extends N
? Ret
: Arr extends [infer _1, ...infer Rest extends any[]]
? KeepFirstN<N, Rest, [...Ret, _1], Add1<Kept>>
: [];
type SliceNoNeg<Arr extends any[], From, To> = RemoveFirstN<
From,
Arr
> extends infer Res extends any[]
? KeepFirstN<To, Res, [], From>
: never;
type ConvertToPositive<N, X extends number> = IsNeg<X> extends true
? MinusXY<N, Abs<X>>
: X;
type Slice<
Arr extends any[],
From extends number = 0,
To extends number = Arr["length"]
> = SliceNoNeg<
Arr,
ConvertToPositive<Arr["length"], From>,
ConvertToPositive<Arr["length"], To>
>;
所以 ¶
这类型体操纯属在折磨自己了吧