字符串的长度
字符串的长度
题目
计算字符串字面量的长度,行为类似 String#length。
type T0 = LengthOfString<"hello"> // 5
type T1 = LengthOfString<""> // 0
type T2 = LengthOfString<"typescript"> // 10解题
type LengthOfString<
S extends string,
T extends string[] = []
> = S extends `${infer F}${infer R}`
? LengthOfString<R, [...T, F]>
: T['length']核心思路:TypeScript 类型系统无法直接读取字符串字面量的 .length 数值——但元组的 .length 是精确的数字字面量类型。所以我们把字符串逐字符转成元组,再返回 T['length']。
逐步拆解
第一步 — 模板字面量 infer 分割
S extends `${infer F}${infer R}`当 S = "hello" 时,TypeScript 推断 F = "h",R = "ello"。每次调用剥掉第一个字符,剩余部分继续递归。当 S = "" 时,模式匹配失败,走到 base case。
第二步 — 累积到元组
LengthOfString<R, [...T, F]>每个字符都追加到累积器 T 中。处理 "hello" 的过程:
- 第 0 步:
T = [],S = "hello" - 第 1 步:
T = ["h"],S = "ello" - 第 2 步:
T = ["h","e"],S = "llo" - 第 3 步:
T = ["h","e","l"],S = "lo" - 第 4 步:
T = ["h","e","l","l"],S = "o" - 第 5 步:
T = ["h","e","l","l","o"],S = ""
第三步 — 读取长度
当 S = "" 时,条件分支走 base case,返回 T['length']。元组长度是数字字面量类型,所以 ["h","e","l","l","o"]['length'] 求值为 5。
深度解析
为什么不能直接用 S['length']?
JavaScript 中 "hello".length === 5,但在 TypeScript 类型系统里:
type L = "hello"['length'] // string(不是 5!)TypeScript 知道字符串有 .length 属性,但类型是 number,而不是具体的数字字面量。类型系统本身不追踪字符串的字符数量。
元组则不同,它的长度是精确的数字字面量:
type L = ["h","e","l","l","o"]['length'] // 5 ✅因为元组类型在结构上是精确的——TypeScript 知道 ["h","e","l","l","o"] 恰好有 5 个元素。
累积器模式
T extends string[] = [] 是递归类型工具中经典的累积器(accumulator)模式。不是在递归回溯时构建结果,而是把状态向前传递:
// ❌ 无累积器 — 无法在类型层面做算术加法
type Bad<S extends string> = S extends `${infer F}${infer R}`
? 1 + Bad<R> // TypeScript 不支持这样的类型算术
: 0
// ✅ 有累积器 — 长度自然从元组中读取
type Good<S extends string, T extends string[] = []> =
S extends `${infer F}${infer R}`
? Good<R, [...T, F]>
: T['length']这个模式避免了对类型做算术,让元组本身充当计数器。
递归深度限制
TypeScript 的递归深度上限约为 ~1000 层。由于每个字符消耗一层递归,这个方案对长度 ≤ ~1000 的字符串字面量完全够用,实际场景几乎不会遇到限制。
infer F 为什么只匹配一个字符?
`${infer F}${infer R}` 中,F 为什么是单个字符而不是多个?
TypeScript 对模板字面量中的 infer 采用贪心+回溯策略:前面的 infer 尽量少匹配,后面的 infer 接收剩余部分。对于无分隔符的两段 infer,结果固定为 F = 第一个字符,R = 剩余字符串,这是有保证的行为。
关键收获
- 字符串字面量没有数字级别的长度类型,但元组有
T['length'] - 累积器模式:通过递归传递不断增长的元组,在 base case 读取其长度
`${infer F}${infer R}`可靠地剥离字符串字面量的第一个字符- 这个技巧可以推广:凡是需要"计数"字符串字符的场景,先转成元组再处理
