# 34286-Take
# 题目描述
实现 Take<N, Arr>,从元组 Arr 中取头 N 个元素;若 N 是负数,则取末尾 |N| 个。
type T0 = Take<2, [1, 2, 3]>; // [1, 2]
type T1 = Take<3, ['1', 2, true, false]>; // ['1', 2, true]
type T2 = Take<-2, [1, 2, 3]>; // [2, 3]
type T3 = Take<0, [1, 2, 3]>; // []
type T4 = Take<5, [1, 2, 3]>; // [1, 2, 3]
type T5 = Take<3, []>; // []
# 分析
分两种情况:
N ≥ 0:从头往后拆,拆到拿满N个或元组空为止。N < 0:等价于"取末尾|N|个"——可以对元组先 reverse、取头|N|个、再 reverse 回来;或者直接用[...infer Init, infer Last]从末尾拆。
判断 N 是不是负数最稳的办法是 `${N}` extends `-${infer M extends number}`——既一步拿到绝对值,又能把 |N| 送进正数分支。
# 题解
type TakeHead<
N extends number,
Arr,
Acc extends unknown[] = [],
> = Acc['length'] extends N
? Acc
: Arr extends [infer F, ...infer R]
? TakeHead<N, R, [...Acc, F]>
: Acc;
type TakeTail<
N extends number,
Arr,
Acc extends unknown[] = [],
> = Acc['length'] extends N
? Acc
: Arr extends [...infer Init, infer L]
? TakeTail<N, Init, [L, ...Acc]>
: Acc;
type Take<N extends number, Arr> = `${N}` extends `-${infer M extends number}`
? TakeTail<M, Arr>
: N extends number
? TakeHead<N, Arr>
: never;
解读:
TakeHead<N, Arr, Acc>:累加器Acc每轮 push 头部元素,长度到N就停。如果元组先空了就返回Acc(处理N > 元组长度的情况)。TakeTail<N, Arr, Acc>:从Arr末尾拆元素,每轮把末尾元素 头插 到Acc,保持最终顺序正确。- 主体根据字符串前缀区分正负号:负号就走
TakeTail并把|N|丢进去,正号走TakeHead。
# 验证
type cases = [
Take<2, [1, 2, 3]>, // [1, 2]
Take<3, ['1', 2, true, false]>, // ['1', 2, true]
Take<-2, [1, 2, 3]>, // [2, 3]
Take<0, [1, 2, 3]>, // []
Take<5, [1, 2, 3]>, // [1, 2, 3]
Take<3, []>, // []
];
# 知识点
`${N}` extends `-${infer M extends number}`既能判断负数又能一步拿到绝对值,是所有带负数参数题目的通用入口,见 冷门-字面量类型和基础类型。TakeTail里的[L, ...Acc](头插)和TakeHead里的[...Acc, F](尾插)顺序相反——这对应"从哪端拆、累加器朝哪个方向生长",在所有元组题里都要想清楚。Acc['length'] extends N作为循环出口是元组递归的标配,见 进阶-计数-加减乘除。