You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何生成包含所有小写字母的字面量联合类型?是否可通过递归类型定义实现更优雅的方案?

如何用递归类型定义生成所有小写字母的字面量联合类型?

问题描述

我需要定义一个包含所有小写字母的字面量联合类型 "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;

原理拆解:

  1. CharCodeToString:做一个硬映射,把每个小写字母对应的ASCII数字转换成字面量类型;
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 06:07:30