如何生成包含所有小写字母的字面量联合类型?是否可通过递归类型定义实现更优雅的方案?
如何用递归类型定义生成所有小写字母的字面量联合类型?
问题描述
我需要定义一个包含所有小写字母的字面量联合类型 "a" | "b" | ... | "z",目前已经有了一种可行的实现方式——通过只读数组提取元素类型:
const alphabet = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"] as const type Letter = typeof alphabet[number]
不过我好奇,有没有更优雅的实现方式?比如通过递归类型定义来得到同样的Letter类型?
回答
当然可以用递归类型来实现!不过得结合TypeScript的条件类型、模板字符串类型,以及字符ASCII码的特性来完成。下面给你两种递归实现思路:
方案一:基于ASCII码映射的递归生成
这是最稳定的递归实现方式,原理是利用小写字母的ASCII码范围(a=97,z=122),从起始码开始递归生成到结束码:
// 把ASCII码数字映射为对应的小写字母字面量 type CharCodeToString<C extends number> = C extends 97 ? "a" : C extends 98 ? "b" : C extends 99 ? "c" : C extends 100 ? "d" : C extends 101 ? "e" : C extends 102 ? "f" : C extends 103 ? "g" : C extends 104 ? "h" : C extends 105 ? "i" : C extends 106 ? "j" : C extends 107 ? "k" : C extends 108 ? "l" : C extends 109 ? "m" : C extends 110 ? "n" : C extends 111 ? "o" : C extends 112 ? "p" : C extends 113 ? "q" : C extends 114 ? "r" : C extends 115 ? "s" : C extends 116 ? "t" : C extends 117 ? "u" : C extends 118 ? "v" : C extends 119 ? "w" : C extends 120 ? "x" : C extends 121 ? "y" : C extends 122 ? "z" : never; // 递归生成字母联合类型,默认从a(97)到z(122) type GenerateLetters<Start extends number = 97, End extends number = 122> = Start extends End ? CharCodeToString<Start> : CharCodeToString<Start> | GenerateLetters<Start extends number ? Start + 1 : never, End>; // 最终的小写字母联合类型 type Letter = GenerateLetters;
原理拆解:
CharCodeToString:做一个硬映射,把每个小写字母对应的ASCII数字转换成字面量类型;GenerateLetters:递归核心逻辑——如果当前起始码等于结束码,就返回对应字母;否则把当前字母和下一个码的递归结果做联合,直到遍历到z的ASCII码122为止。
方案二:利用模板字符串的简化递归(谨慎使用)
如果你觉得上面的映射表太冗长,也可以尝试用模板字符串的小技巧(需要TypeScript 4.5+),不过这种方式依赖字符串的递增特性,稳定性不如ASCII码映射:
type GenerateLetters< Current extends string = "a", Accumulator extends string = Current > = Current extends "z" ? Accumulator | "z" : GenerateLetters< `${string}${Current}` extends `${infer _}${infer Next}` ? Next : never, Accumulator | `${string}${Current}` extends `${infer _}${infer Next}` ? Next : never >; type Letter = GenerateLetters;
两种实现 vs 原数组方案的对比
- 原数组方案:优点是极度直观、代码简洁、维护成本低(要扩展大写字母或其他字符,直接修改数组就行);唯一的小缺点是会在运行时生成一个只读数组(不过大部分场景下这完全不是问题)。
- 递归类型方案:优点是纯类型层面实现,不需要运行时变量;缺点是代码更复杂、可读性稍差,而且如果要扩展字符,修改成本更高。
总结
如果追求简洁易维护,原数组方案其实已经非常优雅了,完全够用;如果你一定要纯类型层面的递归实现,方案一的ASCII码映射是最可靠的选择。
内容的提问来源于stack exchange,提问作者kondor
相关产品推荐
相关产品推荐

