- 文档
- 教程
【免费下载链接】typescript-book
The Concise TypeScript Book: A Concise Guide to Effective Development in TypeScript. Free and Open Source.
本文是开源项目 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; }关键结论(该章明确给出):
extends关键字只对 interface 和 class 生效;对 type 别名,组合的方式就是交叉类型:type A = { a: number; }; type B = { b: number; }; type C = A & B;- 可以用 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.
相关推荐
The Concise TypeScript Book 精讲:Intersection Types(交叉类型)——用 `&` 将多个类型组合为一个类型
The Concise TypeScript Book 精讲:Intersection Types(交叉类型)——用 & 将多个类型组合为一个类型 交叉类型(I
文档教程The Concise TypeScript Book 精讲:对象类型(Object Types)从定义到组合实战
The Concise TypeScript Book 精讲:对象类型(Object Types)从定义到组合实战 导读 对象类型是 TypeScript 类型
文档教程The Concise TypeScript Book:深入解析交叉类型(Intersection Types)与 `&` 运算符
The Concise TypeScript Book:深入解析交叉类型(Intersection Types)与 & 运算符 导读 交叉类型(Intersec
文档教程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考