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>>
所以 ¶
这类型体操纯属在折磨自己了吧