14.交叉类型 副本.md 来源:https://my.feishu.cn/file/EniWbudWToTHToxcGfwc9uuRnyh 采集状态:已完成正文采集;已从查看代码模式逐行提取,行号 1 至 126 连续,并核对滚动到底。 在 TypeScript 的类型系统中,联合类型 (`|`) 给了我们“或”的选择,允许一个值成为多种类型之一。而与之相对的,则是提供了“与”能力的**交叉类型 (Intersection Types)**。它允许我们将多个类型合并(或“交叉”)成一个单一的、拥有所有类型成员的新类型。 交叉类型是实现“组合优于继承”设计原则的强大工具,特别适合用于创建复杂的、由多个部分组合而成的类型。 ### 什么是交叉类型? 交叉类型使用 `&` 运算符,将多个已存在的类型组合在一起。最终生成的类型将包含所有原始类型的所有属性。 其基本语法如下: ```typescript type TypeA = { a: string }; type TypeB = { b: number }; type CombinedType = TypeA & TypeB; // CombinedType 现在等价于: // { // a: string; // b: number; // } ``` 任何被声明为 `CombinedType` 的对象,都必须同时拥有 `a` 和 `b` 两个属性,并符合其各自的类型要求。 让我们看一个更实际的例子。假设我们正在为一个 CRM 系统建模,一个客户的完整资料由“联系信息”和“业务信息”两部分组成: ```typescript type ContactInfo = { email: string; phone: string; }; type BusinessInfo = { companyName: string; vatNumber: string; }; // 使用交叉类型组合成一个完整的客户档案 type CustomerProfile = ContactInfo & BusinessInfo; const customer: CustomerProfile = { email: "contact@example.com", phone: "123-456-7890", companyName: "Example Inc.", vatNumber: "VAT123", }; ``` 通过 `&` 运算符,`CustomerProfile` 类型现在同时拥有了 `ContactInfo` 和 `BusinessInfo` 的**所有**属性。这种方式清晰地表达了“一个完整的客户档案是由联系信息和业务信息共同组成的”这一业务逻辑。 ### 当原始类型相交时 一个有趣且重要的知识点是:当原始类型(如 `string`, `number`)进行交叉时会发生什么? ```typescript type ImpossibleType = string & number; ``` `ImpossibleType` 的类型是 `never`。这是因为在类型系统中,一个值不可能**同时**是 `string` **且**是 `number`。`never` 类型正是用来表示这种永远不会发生的、不合逻辑的类型状态。这个特性在进行类型推导和确保类型安全时非常有用。 ### 交叉类型 (`&`) vs. 接口扩展 (`extends`) 初学者可能会对 `&` 和 `extends` 感到困惑,因为它们都能实现类型的扩展。但它们的语义和适用场景有着本质的区别。 | 特性 | `interface extends ...` (接口扩展) | `type ... & ...` (交叉类型) | | :------- | :------------------------------------------------------------------- | :--------------------------------------------------------------------------- | | **语义** | **继承 (Inheritance)**。表达一种 “is-a” (是一个) 的层次关系。例如,`Admin extends User` 意味着 `Admin` 是一种特殊的 `User`。 | **组合 (Composition)**。表达一种 “has-a” (拥有) 的聚合关系,将多个独立的类型特征组合在一起。 | | **使用场景** | 构建清晰的对象层次结构,如 `Square extends Shape`。 | 将不同维度的功能“混入”(Mixin)到一个类型中,如 `Draggable & Clickable`。 | | **来源** | 只能用于 `interface` 之间。 | 可以用于 `type` 或 `interface` 的任意组合。 | **简单来说:** * 当你能说 “A 是一种 B” 时,使用 `extends`。 * 当你需要将多个独立的功能块组合成一个新的整体时,使用 `&`。 ### 实践应用:实现混入 (Mixin) 模式 交叉类型最强大的应用场景之一是实现**混入 (Mixin)** 模式。Mixin 是一种在不使用类继承的情况下,为对象添加可复用功能的设计模式。 假设我们有两个函数,它们各自为对象添加一种能力:一个添加时间戳,另一个添加日志功能。 ```typescript type HasTimestamp = { timestamp: number; }; type HasLogger = { log(message: string): void; }; // 这个函数为一个对象添加时间戳能力 function withTimestamp(obj: T): T & HasTimestamp { return { ...obj, timestamp: Date.now() }; } // 这个函数为一个对象添加日志能力 function withLogger(obj: T): T & HasLogger { return { ...obj, log: (message: string) => console.log(`[LOG] ${message}`), }; } // 基础对象 const basicObject = { name: "My Component", }; // 使用函数组合和交叉类型来构建一个功能丰富的对象 const composedObject = withLogger(withTimestamp(basicObject)); // 现在 composedObject 的类型是 { name: string } & HasTimestamp & HasLogger console.log(composedObject.name); console.log(composedObject.timestamp); composedObject.log("Component initialized."); ``` 在这个例子中,`composedObject` 的最终类型是 `basicObject` 的原始类型与 `HasTimestamp` 和 `HasLogger` 的交叉。TypeScript 能够完美地理解这个组合后的新类型,并为我们提供完整的类型检查和代码提示。这是一种极其灵活且类型安全的组合方式。 ### 总结 交叉类型是 TypeScript 中实现“组合”思想的核心工具。它使我们能够以一种声明式的方式,将多个独立的类型合并成一个功能更强大的复合类型。 * 它使用 `&` 运算符,将所有成员类型的属性聚合在一起。 * 它与 `extends` 的继承关系不同,更侧重于功能的**组合**与**聚合**。 * 在实现混入 (Mixin) 等高级设计模式时,交叉类型是不可或缺的强大工具。 当你需要构建一个由多个独立部分“拼装”而成的复杂类型时,请记住交叉类型。它将帮助你创建出结构清晰、精确且富有表现力的类型定义。