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 不确定你拿到的是哪种。必须先用 typeofinstanceofin 等手段"收窄"出具体类型。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.radiuss.sizes.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 交叉:一句话区分

小结

联合(|)表示"或",交叉(&)表示"且"。处理联合的核心技巧是类型收窄typeof/instanceof/in)。可辨识联合是 TS 最强大的模式之一,强烈推荐在状态管理、错误处理里使用。下一篇讲 TS 的类与面向对象

← 上一篇 TypeScript 泛型

下一篇 TypeScript 类

✈️💬