TypeScript 联合与交叉类型
联合类型和交叉类型是 TS 组合复杂类型的两把瑞士军刀。联合(|)表示"或"——一个值可以是几种类型之一;交叉(&)表示"且"——把多个类型合并成一个。这一章讲清楚它们的写法和实战用法。
1. 联合类型 |(或)
联合类型用一个竖线 | 连接多个类型,表示"值可以是其中任意一种":
// 联合类型 | :一个值可以是多种类型之一
type ID = number | string;
function formatId(id: ID): string {
return String(id); // number 和 string 都能转成 string
}
formatId(123);
formatId("abc-001");
// 字面量联合:最常见的用法,描述"取值有限"的场景
type Status = "idle" | "loading" | "success" | "error";
let page: Status = "loading"; // 只能是这四个值之一字面量联合(如 Status)是日常最常用的形态——它相当于"超级枚举",比 enum 更轻量,调试更友好。
2. 类型收窄(narrowing)—— 处理联合的关键
联合类型的值,不能直接当任意一种类型来用——因为 TS 不确定你拿到的是哪种。必须先用 typeof、instanceof、in 等手段"收窄"出具体类型。TS 会跟着你的判断自动识别:
// 处理联合类型的核心技巧:类型收窄(narrowing)
function format(input: string | number): string {
// 用 typeof 把类型"收窄"到具体的一种
if (typeof input === "number") {
// 这里 TS 知道 input 是 number,可以用数字方法
return input.toFixed(2);
}
// 这里 TS 知道 input 是 string
return input.trim();
}
// 三种常见的收窄方式
// 1. typeof(基础类型)
typeof x === "string"
// 2. instanceof(类)
x instanceof Date
// 3. in(属性是否存在)
"size" in x这是 TS 最贴心的特性之一:你写的普通 if/switch 判断,TS 会据此自动收窄类型,让代码既安全又简洁。下一篇"类型守卫"会更深入讲这个。
3. 交叉类型 &(且)
交叉类型用一个 & 连接多个类型,表示"合并所有属性"。结果是必须同时满足所有类型的全部字段:
// 交叉类型 & :把多个类型合并成一个(全部属性都要有)
type WithTime = { createdAt: string };
type WithAuthor = { author: string };
type Article = WithTime & WithAuthor;
const post: Article = {
createdAt: "2025-08-05",
author: "小明",
};
// 多个交叉:每个类型的属性都要满足
type A = { x: number };
type B = { y: number };
type C = { z: number };
type ABC = A & B & C; // 必须同时有 x, y, z交叉类型常用于混入(mixin)——把多个能力组合成一个对象类型。
4. 可辨识联合 —— TS 的杀手锏
这是 TS 最强大的类型模式之一。给联合的每个成员加一个共同的字面量标签属性,TS 就能根据这个标签自动收窄出具体类型,写出绝对安全的分支逻辑:
// 可辨识联合(discriminated union):TS 的杀手锏之一
// 每个 variant 都有一个共同的字面量属性(叫"标签")
type Shape =
| { kind: "circle"; radius: number }
| { kind: "square"; size: number }
| { kind: "rect"; w: number; h: number };
function area(s: Shape): number {
// 根据 kind 这个标签,TS 能自动收窄出具体类型
switch (s.kind) {
case "circle": return Math.PI * s.radius ** 2;
case "square": return s.size ** 2;
case "rect": return s.w * s.h;
}
}
area({ kind: "circle", radius: 2 }); // 12.566...
area({ kind: "square", size: 3 }); // 9注意每个分支里 s.radius、s.size、s.w 都被 TS 精确识别——这就是"可辨识联合"的威力。前端的状态管理、API 的错误处理、配置对象,都强烈推荐用这种模式写。
5. 实战:用户角色权限
把可辨识联合用到一个真实场景:不同角色的用户有不同的字段,根据 role 标签做分支:
// 实战:用户角色权限
type Admin = { role: "admin"; permissions: string[] };
type Editor = { role: "editor"; editableCategories: string[] };
type Viewer = { role: "viewer" };
type User = Admin | Editor | Viewer;
function canEdit(user: User): boolean {
if (user.role === "admin") return user.permissions.includes("edit");
if (user.role === "editor") return user.editableCategories.length > 0;
return false; // viewer 不能编辑
}这种写法的好处:如果未来新增角色(比如 "guest"),TS 会在 canEdit 函数里报错,提醒你补上分支——编译期就帮你发现遗漏。
6. 联合 vs 交叉:一句话区分
- 联合
A | B:取值可以是 A 或 B(范围"变宽",属性变少)。 - 交叉
A & B:必须同时满足 A 和 B(范围"变窄",属性变多)。
小结
联合(|)表示"或",交叉(&)表示"且"。处理联合的核心技巧是类型收窄(typeof/instanceof/in)。可辨识联合是 TS 最强大的模式之一,强烈推荐在状态管理、错误处理里使用。下一篇讲 TS 的类与面向对象。
← 上一篇 TypeScript 泛型
下一篇 TypeScript 类 →