- 文档
- 教程
【免费下载链接】typescript-book
The Concise TypeScript Book: A Concise Guide to Effective Development in TypeScript. Free and Open Source.
字面量类型(Literal Types)是 TypeScript 类型系统中"最精确"的一类类型:它把某个具体的 JavaScript 原始值(一个字符串、一个数字或一个布尔值)本身上升为一种类型。本篇指南以 The Concise TypeScript Book 意大利语版的 Tipi letterali 章节为主体,结合仓库中 字面量推断、联合类型、类型收窄、可辨识联合、穷尽性检查、模板联合类型 与 类型系统探索 等章节深入展开。读完你将掌握字面量类型的定义、推断规则,以及如何用它构建类型安全的联合类型、可辨识联合、穷尽性检查等实战模式。
什么是字面量类型:类型即集合视角下的"单元素集合"
在 The Concise TypeScript Book 中,字面量类型的定义非常精炼:
一个字面量类型是集合类型(collective type)内部的单元素集合(single-element set);它定义了一个非常精确的值,而这个值就是一个 JavaScript 原始值。
这句话背后是 TypeScript 的"类型即集合"思想。仓库的 Esplorazione del sistema di tipi 章节中明确写道:"在 TypeScript 中,一个类型是可能值的集合,该集合也被称为类型的域(domain)。类型中的每个值都可以看作集合中的一个元素。"该章节还给出了一张集合论术语与 TypeScript 类型的对照表:
| 集合术语 | TypeScript | 说明 |
|---|---|---|
| 空集 | never | never中不包含任何值 |
| 单元素集合 | undefined/null/字面量类型 | 恰好只有一个值 |
| 有限集合 | boolean/ 联合类型 | 有限个值 |
| 无限集合 | string/number/object | 无穷多个值 |
| 全集 | any/unknown | 每个元素都是any的成员 |
由此可见:string是"所有字符串组成的无限集合",而'a'(字面量类型)是"恰好只有'a'这一个字符串的单元素集合"。字面量类型把类型从"宽泛的分类"收敛到"精确的常量",这正是它被用于约束取值范围的根基。
三种字面量类型:字符串、数字、布尔
TypeScript 中的字面量类型只有三类,全部对应 JavaScript 原始值:
- 字符串字面量类型(String Literal Types):如
'a' - 数字字面量类型(Numeric Literal Types):如
1 - 布尔字面量类型(Boolean Literal Types):如
true
原文档给出的基础示例:
const a = 'a'; // 字符串字面量类型 const b = 1; // 数字字面量类型 const c = true; // 布尔字面量类型注意:上例中三个变量都用const声明,因此它们的类型会被推断为精确的字面量类型'a'、1、true,而不是宽泛的string、number、boolean。这正是下一节"字面量推断"要讲的核心规则。
字面量推断:为什么const与let推断结果不同
字面量类型与类型推断(Inference)紧密相关。仓库的 Inferenza letterale 章节解释了 TypeScript 的关键推断行为:
字面量推断(Literal Inference)是 TypeScript 的一项特性,它允许根据变量的值推断出变量的类型。
const x = 'x'; // 字面量类型 'x',因为这个值此后无法改变 let y = 'y'; // 类型 string,因为我们可以随时修改这个值推断规则:const声明的变量不可重新赋值,所以 TypeScript 敢于把类型收窄为精确的字面量类型;而let声明的变量随时可被改成其他字符串,所以会被拓宽(widening)为string。
一个更隐蔽的陷阱出现在对象属性上:即使属性值看起来是字面量,TypeScript 默认也认为它可以被后续修改,因此推断为宽泛类型:
type X = 'a' | 'b'; let o = { x: 'a', // 这是宽泛的 string,而不是字面量 'a' }; const fn = (x: X) => `${x}-foo`; console.log(fn(o.x)); // 报错:类型 'string' 不能赋给参数类型 'X'由于o.x被推断为string(而非字面量'a'),把它传给只接受X的函数时 TypeScript 会报错——X是比string更窄(narrower)的类型。解决方式有两种,都是类型断言(type assertion):
let o = { x: 'a' as const, // 断言为字面量 'a' };let o = { x: 'a' as X, // 断言为联合类型成员 'a' };除了as const与as X这类局部断言,仓库 Esplorazione del sistema di tipi 章节还介绍了整体断言as const,以及 TypeScript 5.0 起在泛型参数上使用const修饰符来保留字面量精度,我们会在后文展开。
组合字面量:联合类型与类型别名
单独的字面量类型价值有限,它们的真正威力在于组合成联合类型(Union Types)。仓库的 Union Type 章节定义:
联合类型是表示一个值可以是若干类型之一的类型,用
|符号连接每种可能的类型。
let x: string | number; x = 'hello'; // 合法 x = 123; // 合法当字面量类型通过|组合时,就得到一个"取值白名单"。原文档的核心示例:
type O = 'a' | 'b' | 'c';O只包含'a'、'b'、'c'这三个明确指定的字符串,任何其他字符串都不合法。用集合论的视角看(见 类型系统探索 的"Tipi come insiemi"小节),'a' | 'b' | 'c'就是三个单元素集合的并集(T1 ∪ T2);而T1 extends T2相当于"T1 是 T2 的子集"。
这种"字面量联合"是 TypeScript 表达有限状态集合最自然的方式,比纯字符串类型安全得多:编辑器能给出精确的自动补全,编译器能在赋值时拦截拼写错误,例如:
type O = 'a' | 'b' | 'c'; const x: O = 'A'; // 报错:类型 '"A"' 不能赋给类型 'O'实战一:用字面量做判别式——可辨识联合(Discriminated Unions)
字面量类型最常见的实战价值,是作为可辨识联合(Discriminated Unions)的判别属性(discriminant)。仓库的 Unioni discriminate 章节定义:
可辨识联合是一种联合类型,它利用一个公共属性(称为判别式)来收窄联合中可能的类型集合。
type Square = { kind: 'square'; // 判别式 size: number; }; type Circle = { kind: 'circle'; // 判别式 radius: number; }; type Shape = Square | Circle; const area = (shape: Shape) => { switch (shape.kind) { case 'square': return Math.pow(shape.size, 2); case 'circle': return Math.PI * Math.pow(shape.radius, 2); } }; const square: Square = { kind: 'square', size: 5 }; const circle: Circle = { kind: 'circle', radius: 2 }; console.log(area(square)); // 25 console.log(area(circle)); // 12.566370614359172这里的kind属性类型是字面量联合'square' | 'circle'。在switch的case 'square'分支内,TypeScript 会把shape精确收窄为Square,因此可以安全访问shape.size;同理case 'circle'分支内可访问shape.radius。判别式必须使用字面量类型才能触发这种收窄——这正是字面量类型"精确性"的直接体现。
实战二:配合类型收窄(Narrowing)与类型保护(Type Guards)
字面量联合类型与**类型收窄(Narrowing)**天然互补。仓库的 Restringimento 章节定义:
TypeScript 的收窄是在条件块内精化变量类型的过程,在处理联合类型时尤其有用。
该章节列举了多种收窄方式,其中与字面量类型直接相关的有:
相等性收窄(Equality Narrowing)——配合switch与===、!==、==、!=等运算符,按字面量值收窄类型:
const checkStatus = (status: 'success' | 'error') => { switch (status) { case 'success': return true; case 'error': return null; } };switch的每个case本质上都在与字面量'success'、'error'做相等比较,TypeScript 借此把status收窄到对应的字面量单元素类型。
typeof 收窄(typeof guards)——按 JavaScript 运行时类型收窄,适合原始值联合:
const fn = (x: number | string) => { if (typeof x === 'number') { return x + 1; // 此处 x 是 number } return -1; };真值收窄(Truthiness Narrowing)——通过变量真假判断收窄:
const toUpperCase = (name: string | null) => { if (name) { return name.toUpperCase(); } else { return null; } };in运算符收窄与instanceof收窄——分别按属性存在性与构造函数实例判断收窄:
type Dog = { name: string; breed: string; }; type Cat = { name: string; likesCream: boolean; }; const getAnimalType = (pet: Dog | Cat) => { if ('breed' in pet) { return 'dog'; } else { return 'cat'; } };class Square { constructor(public width: number) {} } class Rectangle { constructor( public width: number, public height: number ) {} } function area(shape: Square | Rectangle) { if (shape instanceof Square) { return shape.width * shape.width; } else { return shape.width * shape.height; } } const square = new Square(5); const rectangle = new Rectangle(5, 10); console.log(area(square)); // 25 console.log(area(rectangle)); // 50此外,类型系统探索 章节还介绍了Array.isArray()等内置守卫,以及当内置手段不够用时,可以用**用户自定义类型保护(type predicates)**来收窄——它本质上是返回布尔值并带有value is T签名的函数。字面量类型是这些收窄操作中最细粒度的"终点类型"之一。
实战三:穷尽性检查(Exhaustiveness Checking)与never
字面量联合类型加上never类型,可以实现著名的穷尽性检查(Exhaustiveness Checking)。仓库的 Controllo di esaustività 章节的定义与示例:
穷尽性检查是 TypeScript 的一项特性,它保证可辨识联合的所有可能分支都在
switch或if中被处理。
type Direction = 'up' | 'down'; const move = (direction: Direction) => { switch (direction) { case 'up': console.log('Spostamento verso l\'alto'); break; case 'down': console.log('Spostamento verso il basso'); break; default: const exhaustiveCheck: never = direction; console.log(exhaustiveCheck); // 这行永远不会执行 } };原理:当'up'与'down'都被case处理完后,default分支里direction的类型已被收窄为never。把never赋给never是合法的,因此当前代码编译通过。一旦有人给Direction联合新增成员(比如'left'),default分支中direction的类型就不再是never,const exhaustiveCheck: never = direction就会报错——编译器立刻提醒你新增的分支尚未处理。这是字面量联合 +never构成的"编译期兜底网络",保证状态演进时不会漏掉任何 case。
实战四:模板字面量联合类型(Template Union Types)
字面量类型还能与**模板字面量类型(Template Literal Types)**结合,在类型系统内直接拼接、生成新的字面量联合。仓库的 Tipi di unione di template 章节示例:
type Status = 'active' | 'inactive'; type Products = 'p1' | 'p2'; type ProductId = `id-${Products}-${Status}`; // 结果为:"id-p1-active" | "id-p1-inactive" | "id-p2-active" | "id-p2-inactive"模板字面量类型会对嵌入的联合类型做笛卡尔积展开,把2 × 2组合成 4 个字面量成员。这让开发者能在编译期声明并校验诸如路由路径、事件名、ID 格式、CSS 类名等需要"格式约定"的字符串,无需任何运行时校验代码。
让字面量更精确:const断言与泛型const修饰符
前文提到,对象属性默认会被拓宽为宽泛类型。要保留字面量精度,仓库 类型系统探索 章节给出了一套完整的工具链:
对单个属性使用as const:
const v = { x: 3 as const, }; v.x = 3;对整个对象使用as const:
const v = { x: 1, y: 2, } as const;对元组使用as const——数组默认推断为可变的number[],加as const后变成只读元组:
const x = [1, 2, 3]; // number[] const y = [1, 2, 3] as const; // readonly [1, 2, 3]TypeScript 5.0 的泛型const修饰符——在泛型参数上写const,可以让 TypeScript 推断出"最精确的可能类型",从而保留字面量:
// 不使用 const:属性被拓宽 function identity<T>(value: T) { return value; } const values = identity({ a: 'a', b: 'b' }); // 推断结果:{ a: string; b: string; }// 使用 const 修饰符:属性保留为字面量 function identity<const T>(value: T) { return value; } const values = identity({ a: 'a', b: 'b' }); // 推断结果:{ a: "a"; b: "b"; }同时该章节强调:如果希望变量类型始终精确,也可以使用显式类型标注,比如const v: { x: 1 | 2 | 3 },让字面量联合作为目标类型直接约束赋值。
小结:字面量类型在 The Concise TypeScript Book 中的位置
字面量类型(字符串、数字、布尔)是 TypeScript 类型系统中最精确的构建块。在 The Concise TypeScript Book 的体系里,它们贯穿多个核心章节:
- 定义见 Tipi letterali:单元素集合,值是 JavaScript 原始值;
- 推断规则见 Inferenza letterale:
const保留字面量,let/对象属性拓宽,可用as const或as X修复; - 组合方式见 Union Type 与 Tipi di unione di template:
|组成取值白名单,模板字面量自动展开组合; - 应用模式见 Unioni discriminate(判别式)、Restringimento(收窄)、Controllo di esaustività(
never兜底); - 精度控制见 Esplorazione del sistema di tipi(
as const、泛型const修饰符、集合论视角)。
在实际项目中,建议把"需要枚举取值"的字段(状态、方向、类型标签、ID 前缀等)优先声明为字面量联合类型,再配合switch收窄与never穷尽性检查,即可把大量运行期错误提前到编译期拦截——这是 TypeScript 相对 JavaScript 的核心价值之一,也是本书反复强调的实践方向。
- 文档
- 教程
【免费下载链接】typescript-book
The Concise TypeScript Book: A Concise Guide to Effective Development in TypeScript. Free and Open Source.
相关推荐
The Concise TypeScript Book:深入理解 TypeScript 字面量类型(Literal Types)
The Concise TypeScript Book:深入理解 TypeScript 字面量类型(Literal Types) 字面量类型(Literal T
文档教程TypeScript 字面量类型(Literal Types)完全指南:从单元素集合到联合、收窄与实战
TypeScript 字面量类型(Literal Types)完全指南:从单元素集合到联合、收窄与实战 本文以《The Concise TypeScript B
文档教程The Concise TypeScript Book 之模板联合类型(Template Union Types)实战详解
The Concise TypeScript Book 之模板联合类型(Template Union Types)实战详解 本文以《The Concise Ty
文档教程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考