☰
The Concise TypeScript Book 精讲:Intersection Types(交叉类型)从定义到实战
2026/9/27 8:06:29 网站建设 项目流程
  • 文档
  • 教程

【免费下载链接】typescript-book

The Concise TypeScript Book: A Concise Guide to Effective Development in TypeScript. Free and Open Source.

项目地址:https://gitcode.com/gh_mirrors/typ/typescript-book
点击查看免费下载

本文是开源项目 The Concise TypeScript Book 第 33 章「Intersection Types」的深度精讲。你将掌握交叉类型的定义与&语法,理解它与 Union Type、interface 扩展的关系,并学会在实际代码中用它组合对象、构造含歧义字段的复合类型与 Mixin 模式。所有示例均与仓库中 英文原版章节 及 韩文译版 一一对应,可直接复制运行。

一、什么是交叉类型:定义与核心语法

交叉类型(Intersection Type)是一种表示「同时拥有两个或多个类型全部属性」的值类型,即最终类型的值必须满足被组合的每一个类型的全部约束。TypeScript 使用&符号将多个类型连接起来:

type X = { a: string; }; type Y = { b: string; }; type J = X & Y; // Intersection const j: J = { a: 'a', b: 'b', };
  • type X、type Y各自定义一个只有单一属性的对象类型;
  • type J = X & Y声明了一个交叉类型,J类型的值必须同时包含a: string与b: string两个属性;
  • 因此变量j在赋值时必须同时给出a和b,缺一不可,否则 TypeScript 编译期就会报错。

交叉类型的核心含义是「且」:一个值要想属于X & Y,它就必须既属于X又属于Y。这与数学中的集合交集(intersection)恰好相反——类型作为「值的集合」时(参见 探索类型系统 一章中 "Types as Sets" 的集合论视角),X & Y描述的是两个集合成员的并集属性,而不是取两个值域的交集。这正是初学者最容易困惑的地方:交叉类型组合的是类型的成员约束,而非值集合的公共部分。

与并集类型(Union Type)的对比

交叉类型常与并集类型(Union Type)对照学习。并集类型用|表示「或」关系:值可以是其中任意一个类型:

let x: string | number; x = 'hello'; // Valid x = 123; // Valid

两者的区分要点:

维度交叉类型&并集类型|
语义且:必须满足所有类型约束或:满足其中任意一个即可
属性效果合并所有类型的属性(叠加)保留各成员类型,使用时需收窄
典型场景组合多个对象/接口的结构表示「多选一」的值域

在 Type Manipulation 一章中,两者被并列为「从已有类型创建新类型」的两种基本手段:&用于把多个类型合并为一个,|用于定义一个可以是若干类型之一的新类型。二者是 TypeScript 类型组合的两大基石。

二、交叉类型的本质:为什么它是「结构叠加」

交叉类型之所以能组合出同时包含全部属性的类型,根源于 TypeScript 的结构类型系统(Structural Typing)。在 Erased Structural Types 一章中明确指出:TypeScript 的对象类型并不要求与某个精确类型一一对应,只要一个对象满足某接口的全部要求,它就可以被用在需要该接口的任何位置——即便两者之间没有显式关联。

这一特性让交叉类型在实际代码中表现得非常宽容且强大:

type A = { foo: number }; type B = { bar: string }; type C = A & B; const obj: C = { foo: 42, bar: 'hello' }; // 必须同时提供两个属性

只要对象字面量同时具备foo与bar两个属性,它就满足A & B的全部约束,无需任何继承或显式声明关系。也正是因为结构类型在编译后被完全擦除(erased),交叉类型不会产生任何运行时开销,它只是编译期用于校验类型约束的一种静态构造。

将交叉类型拆开使用

交叉类型本身是一个类型别名(type alias),因此可以把它继续当作普通类型使用——把它作为函数参数类型、变量类型,或继续与其他类型交叉:

type Name = { name: string }; type Age = { age: number }; type Person = Name & Age; // 等价于 { name: string; age: number } function greet(p: Person): string { return `${p.name} is ${p.age} years old`; } const p: Person = { name: 'Simon', age: 17 }; greet(p); // "Simon is 17 years old"

三、与 interface 扩展(extends)的关系

交叉类型和interface的extends是两种实现「类型组合」的路径,但存在明显的能力差异。仓库 Extending Types 一章给出了权威对照:

interface 可以继承(复制其他类型的成员):

interface X { a: string; } interface Y extends X { b: string; }

interface 还可以同时继承多个类型:

interface A { a: string; } interface B { b: string; } interface Y extends A, B { y: string; }

关键结论(该章明确给出):

  1. extends关键字只对 interface 和 class 生效;对 type 别名,组合的方式就是交叉类型:
    type A = { a: number; }; type B = { b: number; }; type C = A & B;
  2. 可以用 interface 继承 type,但不能反过来(type 没有extends能力):
    type A = { a: string; }; interface B extends A { b: string; }

因此,当你想组合两个type别名时,&是唯一选择;而 Differences between Type and Interface 一章进一步补充了二者在扩展语义上的差别:

  • interface 使用extends关键字继承其他 interface 的属性和方法,但interface 无法扩展诸如联合类型这样的复杂类型;
  • 交叉类型则通过&运算符把多个类型合并为一个,能力更自由:
    interface A { x: string; y: number; } type B = A & { j: string; }; const c: B = { x: 'x', y: 123, j: 'j', };

在「组合/交叉能力」上,type显然比interface更灵活:type可以随意使用&组合任意类型(包括联合类型、字面量类型、甚至其他交叉类型),而 interface 的扩展对象被限定为可被extends的结构。这也是 Interface and Type 一章所总结的:type 关键字在定义 Union 和 Intersection 类型时更为灵活。

四、实战场景一:组合零散的对象类型

交叉类型最常见的用途,是把分散定义的对象结构合并成一个完整结构。例如从基础信息与岗位信息组合出员工信息:

type Department = 'dep-x' | 'dep-y'; // Union type Person = { name: string; age: number; }; type Employee = { id: number; department: Department; }; type EmployeeInfo = Person & Employee; // Intersection const info: EmployeeInfo = { name: 'John', age: 30, id: 1001, department: 'dep-x', };

这里EmployeeInfo = Person & Employee将「人的属性」与「员工属性」合并为一个包含 4 个属性的类型。该示例取自 Differences between Type and Interface 一章的 "Union and Intersection Types" 小节,它同时展示了交叉类型可以与联合类型协同工作:department字段本身是一个联合类型,而EmployeeInfo是包含该联合类型的交叉类型。

与索引类型、映射类型协同

交叉类型也可以与其他类型操纵手段组合。例如与keyof索引访问配合:

type Person = { name: string; age: number; email: string; }; // 取两个键,构造「必填 + 可选」的复合配置类型 type RequiredKeys = 'name' | 'age'; type PartialRest = Omit<Person, RequiredKeys>; type UpdatePayload = Pick<Person, RequiredKeys> & Partial<PartialRest>; const payload: UpdatePayload = { name: 'Simon', age: 17, // email 可选 };

这说明&不只是简单拼接,它可以与 Type Manipulation 中的Pick、Partial、Omit等内置工具类型自由嵌套,构造出精准的业务类型。

五、实战场景二:混合同名字段类型(交集语义)

当一个交叉类型中两个成员具有相同名称但不同类型的属性时,结果属性类型是两个成员类型的交叉(交集)。以string与number为例:

type A = { x: string }; type B = { x: number }; type C = A & B; // x 的类型是 string & number

由于string & number没有值能同时满足(既要是 string 又要是 number),该类型实际上等价于never。这意味着:

  • 直接赋值{ x: 'a' }或{ x: 1 }都会产生编译错误;
  • 该属性在类型层面被「压成」不可能存在的值,属于需要刻意避免的歧义设计。

这一行为是集合论语义的直接体现:属性类型的交集在两个不相交的集合上为空。设计类型时应尽量避免交叉成员的属性名冲突;若确实需要,可将冲突字段抽离,或用Omit先剔除再交叉:

type A = { id: string; name: string }; type B = { id: number; createdAt: Date }; // 先剔除冲突字段 id,再合并 type C = Omit<A, 'id'> & B; // { name: string; id: number; createdAt: Date }

与 Mixin 模式的联系

交叉类型的这一「叠加」能力正是 TypeScript Mixin(混入)模式的基石。仓库 Others 一章的 "Mixin classes" 一节即基于交叉类型实现:将多个类的实例结构交叉组合,让一个类同时具备多种能力,是函数式组合与类型系统的经典结合点,也呼应了 Extending Types 中「type 用交叉类型扩展」的结论。

六、编译期行为与注意事项

交叉类型在运行时不存在

交叉类型与并集类型一样,是纯编译期的静态类型构造。TypeScript 在编译时会将这些类型别名擦除,JavaScript 运行时不包含任何&或类型标注痕迹,因此:

  • 交叉类型不会带来任何运行时性能开销;
  • 交叉类型不会自动生成合并后的对象或类,仍需在赋值时显式提供全部属性;
  • 类型别名(包括交叉类型)不支持声明合并(declaration merging),而 interface 支持同名多次声明自动合并,这是 Differences between Type and Interface 一章反复强调的差异。

与never、unknown的交互

结合 探索类型系统 的集合论表格:

  • never是空集:never & T等价于never(空集与任何集合相交仍为空);
  • unknown是全集:unknown & T等价于T(全集与任何集合相交为该集合本身);
  • any是每个集合的超集:any & T的结果在多数情况下会被简化为any。

理解这层集合语义,有助于预测交叉类型在边界情况下的行为,避免写出意外的类型结果。

保持可读性的建议

  • 交叉类型组合的成员越多,结构越难以阅读,建议为每个成员取语义化名称(如type Person = Name & Age & Contact);
  • 优先复用具名 type/interface,避免大段匿名对象直接参与&;
  • 当组合对象出现同名字段时,显式使用Omit处理冲突,保留类型的可预期性。

七、小结

要点结论
定义表示同时拥有多个类型全部属性的值,用&连接各类型
与并集对比&是「且」叠加属性,|是「或」多选一
与 interface 扩展extends只能用于 interface/class;type 组合必须用&;interface 可继承 type,反之不行
同名字段属性类型取交集,不相交时退化为never
运行时编译期擦除,零运行时开销,不可声明合并
典型应用组合对象类型、构造复合配置、Mixin 混入模式

交叉类型是 TypeScript 类型系统的核心组合能力之一。掌握&的语义,并结合 Union Type、Extending Types、Differences between Type and Interface 以及 Type Manipulation 等章节对照学习,你就能在 The Concise TypeScript Book 的完整脉络中,把类型组合技巧熟练运用于真实项目。相关源码与示例均可直接在仓库website/src/content/docs/book/与website/src/content/docs/ko-kr/book/目录下查看与验证。

  • 文档
  • 教程

【免费下载链接】typescript-book

The Concise TypeScript Book: A Concise Guide to Effective Development in TypeScript. Free and Open Source.

项目地址:https://gitcode.com/gh_mirrors/typ/typescript-book
点击查看免费下载
上一篇:Flask-MongoEngine与WTForms集成:快速创建MongoDB模型表单的完整指南
下一篇:告别扫码失败:ZXing如何智能修复倾斜二维码?

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询