讲解
联合类型(union)用竖线连接多个类型,表示「是其中之一」:string | number 表示可以是字符串也可以是数字。它是 TypeScript 表达现实业务的主力工具——接口返回可能成功也可能失败、字段可空、状态有限几种,都是联合类型的用武之地。对联合类型的值做操作,编译器只允许调用所有成员共有的方法;想用某个成员特有的方法,必须先收窄(下一章专题)。
字面量类型(literal type)是把类型精确到某个具体的值:"draft" 是一个类型,表示「只能是字符串 draft」。单个字面量类型意义不大,但把几个字面量联合起来的 "draft" | "published" | "archived" 就是状态机的完美表达——比 string 精确得多,多一个字母都编译不过。数字和布尔也有字面量类型,1 | 2 | 3、true 都是合法写法。
与字面量类型紧密相关的是 const 断言和 let/const 的推断差异:const name = "ts" 推断为字面量类型 "ts"(因为不可再赋值),let name = "ts" 推断为 string(因为还能改)。对象和数组的字面量默认会被「拓宽」(属性推断为 string 而非字面量),想保留字面量精度就用 as const——整个对象变深只读且每个值都是字面量类型。as const 在配置表、常量枚举场景极其常用。
示例
联合类型与共有成员的限制:
function formatId(id: string | number): string {
// 只能调用 string 和 number 共有的方法
return `ID-${id.toString()}`;
}
console.log(formatId("abc"));
console.log(formatId(1024));
字面量联合表达有限状态:
type OrderStatus = "pending" | "paid" | "shipped" | "done";
function nextStatus(s: OrderStatus): OrderStatus {
switch (s) {
case "pending":
return "paid";
case "paid":
return "shipped";
default:
return "done";
}
}
let current: OrderStatus = "pending";
current = nextStatus(current);
// current = "cancelled"; // 若取消注释:不在联合之内,编译报错
console.log(`当前状态:${current}`);
as const 保留字面量精度:
const CONFIG = {
env: "prod",
retries: 3,
} as const;
// CONFIG.env = "dev"; // 若取消注释:深只读,编译报错
type Env = typeof CONFIG.env; // 类型是 "prod" 而不是 string
function describe(env: "dev" | "prod"): string {
return env === "prod" ? "生产环境" : "开发环境";
}
console.log(describe(CONFIG.env), `重试 ${CONFIG.retries} 次`);
常见坑
- 用 string 代替字面量联合:status: string 让 "paied" 这种拼写错误一路漏到运行时;能枚举的值就用字面量联合。
- 对联合类型直接用成员特有方法:value.toUpperCase() 在 string | number 上报错——先收窄(typeof 判断),下一章细讲。
- 忘记 as const 导致类型被拓宽:想拿 "a" | "b" 却写成了普通数组,得到的是 string[];常量表一律 as const。
- 字面量联合与 enum 纠结:简单状态集合优先字面量联合(零运行时开销),枚举章节会对比两者取舍。
小结
联合类型表达「之一」;字面量类型精确到具体值,联合后是状态/选项的最佳表达;as const 冻结推断精度。下一章讲如何让编译器在分支里自动认清具体成员——类型收窄。