TS 类型体操:一个递归模型,拆完 type-challenges 八道 Medium 题
【免费下载链接】weekly前端精读周刊。帮你理解最前沿、实用的技术。项目地址: https://gitcode.com/GitHub_Trending/we/weekly
TypeScript 类型体操里最劝退的,往往不是语法,而是"条件类型 + infer + 递归"三连。我把 type-challenges 题库的 Medium 17~24 这八道题(Permutation、LengthOfString、Flatten、AppendToObject、Absolute、StringToUnion、Merge、KebabCase)逐道抠完,发现它们共用一套很土的递归模型:把输入拆出一个最小单元,存进一个累加器,让输入变小,再递归,直到空了取累加器——说白了就是把 JS 里的一个for循环搬到了类型层面。
这篇 type-challenges 题解按这套模型把八道题分成三组拆完,不预设你读过别的解法;每到一个卡点,我会先把"你以为会怎样"摆出来,再告诉你哪里想当然,最后给正解。
一、先把 for 循环搬进类型层:拆、存、缩
你写 JS 循环大概是这个形状:
let acc: any[] = []; for (let i = 0; i < input.length; i++) { acc.push(process(input[i])); // 存一个 } return acc;类型层面没有let,但有等价物——带默认值的第二个泛型。它在递归里扮演的就是这个acc,我后面会反复叫它"累加器"。把循环翻译成类型,骨架是:
// 把"逐项处理"翻译成类型递归 type Loop<First, Rest, Acc extends unknown[] = []> = Rest extends [infer First, ...infer R] ? Loop<First, R, [...Acc, First]> // 存一个(Acc),缩一个(R) : Acc; // 空了,取累加器三个角色,后面八题全是它的变体,先记住名字:
First:这一步拆出来的最小单元Acc:累加器,默认值([]或never或''),把中间态攒着R:缩小后的输入,递归它
模型立完,下面按输入长什么样分三组。
二、输入是联合类型:逼出分配律,排满每一种可能
题干
type perm = Permutation<'A' | 'B' | 'C'>; // ['A','B','C'] | ['A','C','B'] | ['B','A','C'] | ['B','C','A'] | ['C','A','B'] | ['C','B','A']破题:卡点是"怎么让一个入参递归下去"。TS 有个分配行为——当一个裸泛型出现在T extends U ? A : B里,且T本身是联合类型时,TS 会把T拆成单个成员分别判断,再把结果用|拼回来:
type IsX<T> = T extends 'x' ? 1 : 2; type R = IsX<'a' | 'b'>; // = IsX<'a'> | IsX<'b'> = 2Permutation 正需要这个"拆":递归时把'A'|'B'|'C'摊成'A'、'B'、'C'各自往下走。但分配只在"裸泛型"上触发,而题面只给了一个入参。解法是再声明一个默认值等于第一个的泛型,凑出第二个位置让分配有处下手:
// T:当前要占坑的成员;U:还没排完的全部成员(默认 = T) type Permutation<T, U = T> = [T] extends [never] ? [] // 没人可排,终止 : T extends U ? [T, ...Permutation<Exclude<U, T>>] // 占 T 的坑,剩余成员继续排 : [];这里有个 🔴 想当然的坑。终止条件很多人会写成T extends never ? [] : ...,跑出来直接never,或者压根不展开。原因:TS 对T extends ...做联合分配时有一条特例——当T恰好是never,它会跳过分配、把T原样返回,你的三元判断根本没执行。正解是用元组把T包一层:[T] extends [never]。包上之后分配行为被挡掉,判断才真正跑起来。这一招后面还会用到,先记住"判 never 一律用[T] extends [never]"。
手工展开一层(Permutation<'A'|'B'|'C'>):
T='A'|'B'|'C' 对 T 分配 ─┬─ Permutation<'A','A'|'B'|'C'> ├─ Permutation<'B','A'|'B'|'C'> └─ Permutation<'C','A'|'B'|'C'> 取第一支 Permutation<'A','A'|'B'|'C'>: 'A' extends 'A'|'B'|'C' → ['A', ...Permutation<'B【免费下载链接】weekly前端精读周刊。帮你理解最前沿、实用的技术。项目地址: https://gitcode.com/GitHub_Trending/we/weekly
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考