TypeScript 联合类型与交叉类型深度解析
引言
在 TypeScript 类型系统中,联合类型(Union Types)和交叉类型(Intersection Types)是两种强大的类型组合工具。它们允许开发者基于现有类型创建新的类型组合,极大地增强了类型系统的表达能力。本文将深入探讨这两种类型的特性、使用场景和最佳实践。
联合类型:灵活的类型选择
基本概念
联合类型使用 |
操作符将多个类型组合在一起,表示一个值可以是其中任意一种类型。语法形式为 Type1 | Type2 | ... | TypeN
。
function padLeft(value: string, padding: string | number) {
// ...
}
类型守卫与类型收窄
当处理联合类型时,TypeScript 会要求我们使用类型守卫来缩小类型范围:
function processValue(value: string | number) {
if (typeof value === "string") {
// 这里 value 被收窄为 string 类型
return value.toUpperCase();
} else {
// 这里 value 被收窄为 number 类型
return value.toFixed(2);
}
}
可区分联合(标签联合)
这是一种高级模式,通过共享的字面量类型字段来区分联合中的不同类型:
type Shape =
| { kind: "circle"; radius: number }
| { kind: "square"; sideLength: number };
function getArea(shape: Shape) {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius ** 2;
case "square":
return shape.sideLength ** 2;
}
}
穷尽性检查
TypeScript 可以通过 never
类型帮助我们确保处理了联合类型的所有可能情况:
function assertNever(x: never): never {
throw new Error("Unexpected value: " + x);
}
function getArea(shape: Shape): number {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius ** 2;
case "square":
return shape.sideLength ** 2;
default:
return assertNever(shape); // 如果有未处理的类型会报错
}
}
交叉类型:类型的组合
基本概念
交叉类型使用 &
操作符将多个类型合并为一个类型,新类型包含所有原类型的特性。语法形式为 Type1 & Type2 & ... & TypeN
。
interface Person {
name: string;
}
interface Employee {
id: number;
}
type Staff = Person & Employee;
const staff: Staff = {
name: "Alice",
id: 123
};
实际应用场景
- 混入(Mixins)模式:组合多个类的功能
- 扩展现有类型:添加额外属性而不修改原类型
- 组合多个接口:创建更复杂的类型
function extend<T, U>(first: T, second: U): T & U {
const result = {};
for (const prop in first) {
(result as any)[prop] = first[prop];
}
for (const prop in second) {
(result as any)[prop] = second[prop];
}
return result as T & U;
}
与联合类型的区别
| 特性 | 联合类型 | 交叉类型 | |--------------|-------------------------|-------------------------| | 操作符 | |
| &
| | 语义 | 类型之一 | 所有类型的组合 | | 属性访问 | 只能访问共有成员 | 可以访问所有成员 | | 常见用途 | 处理多种可能的输入 | 组合多个类型的特性 |
高级技巧与最佳实践
- 类型别名与联合/交叉类型结合:提高代码可读性
type Primitive = string | number | boolean;
type Nullable<T> = T | null | undefined;
- 条件类型中的联合与交叉:创建更灵活的类型工具
type NonNullable<T> = T extends null | undefined ? never : T;
-
避免过度使用交叉类型:复杂的交叉类型可能导致类型推断困难
-
优先使用接口扩展而非交叉:对于对象类型,
interface extends
通常比&
更清晰
常见问题解答
Q: 联合类型和交叉类型可以混合使用吗?
A: 可以,例如 (A | B) & (C | D)
表示类型必须满足 (A 或 B)
且 (C 或 D)
的条件。
Q: 如何处理联合类型的数组?
A: 使用括号明确优先级,如 (Type1 | Type2)[]
表示数组元素可以是 Type1 或 Type2。
Q: 交叉类型与继承有何区别?
A: 交叉类型是静态组合,不会创建子类型关系;继承则创建了明确的层次结构。
总结
联合类型和交叉类型是 TypeScript 类型系统中非常重要的工具。联合类型提供了灵活性,允许一个值属于多种类型之一;而交叉类型则允许我们组合多个类型的特性。掌握这两种类型的使用,能够显著提升 TypeScript 代码的类型安全性和表达力。在实际开发中,应根据具体场景选择最合适的类型组合方式,并遵循类型设计的最佳实践。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考