模板字面量类型:用类型做字符串的正则

1. 技术难点:字符串也要有精确类型

普通 TypeScript 里字符串类型就是 string,但它太"宽"------表达不了"必须以某前缀开头""必须是指定的某种格式"这类约束:

  1. 接口路径没法约束 :/api/users/:id,你希望 get('/api/users/123') 合法、get('/api/xxx/123') 报错。
  2. 事件名/命令名拼写 :'onClick' 写成 'onclick' 或 'on-click' 不会报错。
  3. 从联合类型派生字符串 :根据已有类型生成 '${login}_token'、'${key}_enabled' 这种键。

核心难点 :如何让字符串类型的形状可被描述和推导------既能校验输入是否符合格式,又能从类型派生新的字符串类型。模板字面量类型(Template Literal Types)正是为此而生,它让类型系统拥有了类似"字符串正则/模式匹配"的能力。

2. 完整解法:模板字面量类型的四个核心用法

2.1 基本:字符串模式(类似"类型正则")

用反引号模板语法定义字符串的匹配模式 ,${T} 可嵌入其他类型:

ts 复制代码
// 只允许形如 "GET /api/users" 的字符串
type Route = `${'GET' | 'POST'} /${string}`;

const r1: Route = 'GET /api/users';   // ✅
const r2: Route = 'POST /api/users';  // ✅
// const r3: Route = 'DELETE /api/users'; // ❌ DELETE 不在联合里

// 带数字参数
type OrderId = `order_${number}`;
const o1: OrderId = 'order_42';       // ✅
// const o2: OrderId = 'order_abc';    // ❌ 数字匹配不上

直观理解 :${string} 匹配任意字符串,${number} 匹配数字,${'a'|'b'} 匹配指字面量------像正则里的 .*、\d、a|b。

2.2 字符串操作工具:从类型层面操作字符串

模板字面量常和字符串操作类型 组合(Uppercase/Lowercase/Capitalize/Uncapitalize),批量改造键名:

ts 复制代码
// 把对象的键转大写
type UppercaseKeys<T> = { [K in keyof T as Uppercase<K & string>]: T[K] };

interface User { id: number; name: string; }
type UpperUser = UppercaseKeys<User>;
// type UpperUser = { ID: number; NAME: string; }

这里的关键是 as 子句 :映射类型里用 as 可以改键名,结合模板字面量/大写转换实现"键名变换"。

2.3 占位符推导 + infer:做"字符串解析"

模板字面量配合 infer 可以从字符串里提取部分类型(类似正则捕获组):

ts 复制代码
// 提取 "GET /api" 中的方法部分
type ExtractMethod<S extends string> = S extends `${infer M} /${string}` ? M : never;

type M1 = ExtractMethod<'GET /api'>;  // type M1 = 'GET'
type M2 = ExtractMethod<'POST /x'>;   // type M2 = 'POST'

应用场景:解析路由、事件名、命名枚举的前缀,从字符串反推结构。

2.4 实战:强类型"事件总线 / 路由表"

模板字面量最实用的场景------用字符串联合驱动类型,让 API/事件/键名类型联动:

ts 复制代码
// 1. 路由参数强类型: /api/users/5 → userId 是 number
type UserRoute = `/api/users/${number}`;
function getUser(route: UserRoute) { /* ... */ }
getUser('/api/users/123');   // ✅
// getUser('/api/users/abc'); // ❌ 编译期拦截

// 2. 事件名 + 负载联动
type EventMap = {
  'user:login': { userId: number; token: string };
  'user:logout': { userId: number };
};
// 监听时能自动推导对应该事件的负载类型
type EventName = keyof EventMap;
function on<E extends EventName>(event: E, cb: (payload: EventMap[E]) => void) {}
on('user:login', (p) => { p.token; });  // p 自动是 {userId, token}

3. 应用场景

场景 模板字面量类型价值
路由/API 路径 约束路径格式,路径参数类型化
事件总线/命令 事件名拼写检测 + 事件负载联动
键名批量变换 前缀/大小写转换、生成派生键
枚举派生 从联合生成 ${x}_${y} 组合
字符串解析 从文件名/路径提取结构化类型

解决的具体问题:

  • 路由/命令/事件的类型拼写错误
  • 需要从前缀/格式校验字符串
  • 从已有关键字派生新键,让类型跟随业务命名规则

4. 要点总结

  • 模板字面量类型 = 用 ${T} 把一个字符串类型定义成"模式",像类型层面的正则。
  • ${string} / ${number} / ${'a'|'b'} 是三种基础占位,匹配对应类型。
  • 字符串操作类型 (Uppercase/Lowercase 等)+ 映射类型 as 子句,实现键名批量变换。
  • 配合 infer 能从字符串里提取子类型(解析方法名/前缀/路径参数)。
  • 应用落地:路由参数、事件总线、键名派生、命令规范,让业务命名规则被类型"编码"进去。
  • 高级用法涉及条件类型 + infer 组合,可构造复杂字符串 DSL,但别过度------够用即可。

一句话:模板字面量类型让 TS 的字符串类型从"宽泛的 string"变成"可匹配、可推导、可变形的模式",是构建强类型路由、事件、命名规则的核心工具。

相关推荐
FungLeo3 小时前
成为全栈·React 管理后台篇·总复盘:从“接口能调通”到“后台值得使用”
前端·react.js·前端框架·项目复盘·成为全栈
Csvn5 小时前
并发模式:让渲染学会排队、插队和让路
前端
可乐鸡翅yeah_5 小时前
新手梳理:M3U8 线上问题,哪些是前端锅,哪些是后端锅
前端·ios·音视频·实时音视频·m3u8·音视频在线播放
Flynt5 小时前
Linear 用 1000 个 PR 换掉 styled-components,我写了 200 个按钮,把这笔账复现了一遍
前端·css·preact
JavaGuide6 小时前
NVIDIA 又开源了!这次给 AI Agent 加上权限管控
前端·后端
excel6 小时前
prisma 如何处理数据库竞态
前端·数据库·后端
莪_幻尘8 小时前
Skill 体检:30 个 Skill 全凭感觉?体检器先自曝了 8 个“假 0 分
前端·人工智能·llm
风骏时光牛马8 小时前
AI模型综合能力评测:性能、指令遵循与多场景实测对比
前端
Frag0ut8 小时前
Chrome与Chromium内核浏览器在Windows 11上的新特性全景解析
前端·chrome·windows·web安全·chromium·gemini ai·playready drm
hiahiahia1238 小时前
实现完整 Tool Dispatcher
开发语言·前端