TypeScript 的类型系统到底能多强?从 infer 到模板字面量类型,带你玩转高级类型体操
TypeScript 的类型系统是图灵完备的——这意味着你完全可以在类型层面编写程序,就像在值层面一样自由。但很多开发者只用了最简单的类型标注,白白浪费了这颗强大引擎的潜力。今天,我们就从 infer 关键字出发,一路深入到模板字面量类型,最后用一个“类型版 JSON 解析器”来检验你的学习成果。
条件类型与 infer:类型推断的核心工具
条件类型是 TypeScript 类型体操的基石。它就像类型世界的 if/else:
type IsString<T> = T extends string ? true : false;
但单纯的条件类型还不够灵活,搭配 infer 关键字后,你可以从复杂类型中提取出子类型。例如,从函数类型中提取返回值类型:
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
通过 条件类型 与 infer 的组合,你可以模拟出类型层面的模式匹配,从而写出高度抽象的通用类型工具。如果你还不熟悉 TypeScript 的这些高级特性,那么接下来的内容可能会彻底改变你对类型系统的认知。
模板字面量类型:在字符串层面进行模式匹配
模板字面量类型是 TypeScript 4.1 引入的杀手级特性。它让你可以在类型层面操作字符串字面量,就像在运行时操作字符串一样自然。
基础匹配:提取路径参数
假设你有一个路由字符串 'user/:id/profile',你想提取其中的 :id 参数名。利用 模板字面量类型 和 infer,可以轻松做到:
type ExtractParam<T> = T extends `${string}/:${infer P}/${string}` ? P : never;
这个模式看似简单,却能够支撑起整个路由类型的推导。
大小写转换与格式化
另一个常见的场景是转换字符串的命名风格。例如,将 'camelCase' 转为 'snake_case'。借助 模板字面量类型 的递归能力,你可以实现一个类型版的 toSnakeCase:
type ToSnake<T extends string> =
T extends `${infer C}${infer Rest}`
? C extends Uppercase<C>
? `_${Lowercase<C>}${ToSnake<Rest>}`
: `${C}${ToSnake<Rest>}`
: T;
解析复杂字符串模式
当你需要从 'type:user|id:123' 这样的字符串中提取键值对时,模板字面量类型 同样可以胜任——只需多次嵌套条件类型与 infer,就能在类型层面完成字符串解析。
映射类型与键重新映射:从现有类型到新类型
映射类型(Mapped Types)允许你对对象类型的每个属性进行转换。而 TypeScript 4.1 引入的 as 子句,更是实现了键的重新映射(Key Remapping)。结合前面的条件类型,你可以写出极为精确的类型变换。
例如,将所有以 get 开头的属性名改为 set 开头:
type GettersToSetters<T> = {
[K in keyof T as K extends `get${infer P}` ? `set${P}` : K]: T[K]
};
这是 高级类型 中非常实用的技巧,而背后的整套理念都属于 类型体操 的范畴——用类型编程解决实际问题,而不是仅仅为了炫技。
实战:用类型系统解析 JSON
现在我们将所有技巧结合,实现一个类型版的 JSON 解析器。它能够根据输入的 JSON 字符串字面量,静态推导出对应的 TypeScript 类型。
这个解析器会递归使用条件类型、infer、模板字面量类型和映射类型,一层层拆解字符串。比如,输入 '{"name":"Alice","age":30}',输出类型应为 { name: string; age: number }。
编写这样的类型解析器需要深厚的类型功底,如果你在理解复杂类型时遇到瓶颈,不妨借助 GPT 来辅助分析——它能帮你快速解释那些嵌套的类型定义。而 TypeScript 的 高级版 功能如模板字面量类型和条件类型,正是实现这种“类型计算”的基础设施。
常见问题
❓ TypeScript 类型体操适合哪些开发场景?
❓ 模板字面量类型能处理动态拼接的字符串吗?
as const 断言来捕获常量字符串的精确类型。❓ infer 关键字只能在条件类型中使用吗?
infer 必须出现在条件类型的 extends 子句中,用来声明一个待推断的类型变量。它不能单独使用,也不能在泛型约束里使用。






请登录后查看评论内容