# 27133-Square
# 题目描述
给定一个数字 N,返回 N * N。要求支持负数,且能处理至少到 Square<101> = 10201。
type R1 = Square<3>; // 9
type R2 = Square<20>; // 400
type R3 = Square<-5>; // 25
type R4 = Square<101>; // 10201
# 分析
本题看着像普通乘法,实则 非常棘手:
- 类型层做乘法通常借元组长度 (加减乘除);
- 但
101 * 101 = 10201,要造一个长度 10201 的元组,远超 TS 默认 1000 层的尾递归限制。
所以必须想办法降低递归深度:
- 一种思路是每轮 push 多个元素(比如 10 个),把深度降为
N / 10; - 结合
A * B可以拆成 "A 的 B 份拼接",每份由BuildTuple<A>提供,整体深度约为B + BuildTuple<A>的深度。
再叠加 负数:先把符号剥掉,平方完是正数,平方本来就与符号无关。
# 题解
// 去掉负号
type Abs<N extends number> = `${N}` extends `-${infer R extends number}`
? R
: N;
// 每轮 push 10 个,把构造 N 长度元组的深度降到 ~N/10
type BuildFast<N extends number, R extends any[] = []> = R['length'] extends N
? R
: [
...R,
any,
any,
any,
any,
any,
any,
any,
any,
any,
any,
]['length'] extends infer L
? L extends number
? L extends N
? [...R, any, any, any, any, any, any, any, any, any, any]
: L extends infer _ // always true, for nicer format
? BuildFast<N, [...R, any, any, any, any, any, any, any, any, any, any]>
: never
: never
: never;
// 退化版:慢但对边界更稳
type BuildSlow<N extends number, R extends any[] = []> = R['length'] extends N
? R
: BuildSlow<N, [...R, any]>;
// A * B:把 B 个 BuildTuple<A> 拼起来
type Mul<A extends number, B extends number, R extends any[] = []> = B extends 0
? R['length']
: Mul<A, Dec<B>, [...R, ...BuildSlow<A>]>;
type Dec<N extends number> = BuildSlow<N> extends [any, ...infer R]
? R['length']
: 0;
type Square<N extends number> = Mul<Abs<N>, Abs<N>>;
几点说明:
Abs<N>通过模板字符串剥掉前导-,处理负数输入。- 真实提交时可把
BuildSlow替换成BuildFast(每轮 push 10 个);两者逻辑等价,只是后者深度更浅。 Mul是教科书式的"加 B 次 A"。
# ⚠️ 已知局限
本题上游测试集要求 Square<101> = 10201 通过。但 TS 类型系统对元组字面量的大小有硬性上限(约 10000),而 Mul<A, B> 需要物化一个长度 A * B 的元组——101 * 101 = 10201 超出,会触发 TS2799: Type produces a tuple type that is too large to represent。
本文给出的"元组长度法"最高只能稳跑到 Square<100>。要让 Square<101> 及以上跑通,必须改用字符串竖式乘法:把数字拆成字符数组,按位相乘 + 进位再拼回字符串,全程避免物化大元组。这是另一整套实现,本文不展开。
# 验证
type R1 = Square<0>; // 0
type R2 = Square<3>; // 9
type R3 = Square<20>; // 400
type R4 = Square<-5>; // 25
type R5 = Square<-31>; // 961