Yuzhe's Blog

yuzhes

TODO

TODO

题目链接

题目

TODO

解题思路

这道题要求在类型层面构造帕斯卡三角形(杨辉三角)。核心思路与手动构造杨辉三角完全一致:每一行的每个元素等于上一行相邻两个元素之和。

解法分为以下几个层次:

  1. 数字转元组 (FromLength):TypeScript 类型系统没有原生算术运算,因此用 1 的元组来表示数字。FromLength<3> 生成 [1, 1, 1],这是所有类型层面”数学运算”的基础。

  2. 类型层面加法 (Sum):将两个数字分别转为元组,展开合并后读取 length,即可得到两数之和。

  3. 逐元素求和 (SumArr):对两个等长数组逐元素求和。使用递归的头尾解构模式——取出各自的首元素求和,追加到累加器,然后对尾部递归。

  4. 生成下一行 (GetNextRow):这里的巧妙之处在于:给定一行 [1, 3, 3, 1],分别在前后补零得到 [0, 1, 3, 3, 1][1, 3, 3, 1, 0],然后逐元素求和得到 [1, 4, 6, 4, 1]。这个操作完美地实现了杨辉三角的生成规则。

  5. 构建完整三角 (PascalArr):从 [[1]] 开始,反复将最后一行传入 GetNextRow 生成新行并追加。用元组长度作为计数器,每次递归缩减一个元素,长度为 1 时停止。

  6. 入口类型 (Pascal):将数字 N 转为元组后交给 PascalArr 处理。

深度解析

基于元组长度的算术运算

TypeScript 类型系统不支持数值计算,但元组类型提供了一条通道。元组的 ["length"] 属性是数字字面量类型,而展开运算符 [...A, ...B] 可以拼接元组。两者结合就能编码加法:[...FromLength<A>, ...FromLength<B>]["length"] 计算 A + B。这个模式是几乎所有类型层面数学题的基石。

递归条件类型与 infer 的组合

SumArr 展示了一个强大的模式:递归结构解构。条件类型 [A, B] extends [[infer HeadA, ...infer TailA], [infer HeadB, ...infer TailB]] 在单次检查中同时解构两个数组,比分别检查更健壮——在真值分支中可以保证两个数组都非空。

infer ... extends number[] 语法(TypeScript 4.7 引入)在推断时直接收窄类型,避免了额外的条件检查。

GetLast 的索引技巧

GetLast<T> 使用了一个精妙的索引技巧:[never, ...T][T["length"]]。如果 T = [a, b, c](长度为 3),那么 [never, ...T] 就是 [never, a, b, c],在位置 3 处索引得到 c。用一次索引访问取代了完整的递归遍历。

补零滑窗技巧

GetNextRow<[1, 3, 3, 1]> 计算 SumArr<[0, 1, 3, 3, 1], [1, 3, 3, 1, 0]>。通过将同一行分别在首尾补零并错开,逐元素求和自然产生下一行。这与命令式实现中的”滑窗”技巧异曲同工,被优雅地移植到了纯声明式的类型层面。

元组缩减实现计数递减

类型层面没有 N - 1 运算,解法将 N 转为元组,每次迭代用 N extends [1, ...infer Tail] 剥离一个元素。当元组长度为 1 时递归终止。这是类型层面实现倒计数循环的标准模式。

总结