1. 联合类型与类型别名的核心价值
在TypeScript开发中,我们经常需要处理不确定的数据类型或复杂的类型结构。联合类型和类型别名就是为解决这些问题而生的利器。联合类型(Union Types)允许一个值属于多种类型之一,而类型别名(Type Aliases)则让我们能够为复杂的类型定义创建简洁的别名。
提示:在实际项目中,这两种特性通常会结合使用,特别是在处理API响应、配置对象或状态管理时。
1.1 为什么需要联合类型
想象你正在开发一个电商系统,商品ID可能是字符串(如"prod-123")或数字(如123456)。传统的做法可能是使用any类型,但这会失去类型检查的优势。联合类型提供了完美的解决方案:
typescript复制type ProductID = string | number;
function getProduct(id: ProductID): Product {
// 函数实现
}
这种方式的优势在于:
- 明确限制了id参数的类型范围
- 保留了完整的类型检查能力
- 代码可读性更好,一看就知道接受哪些类型
1.2 类型别名的实际意义
当类型定义变得复杂时,直接使用原始类型会让代码难以维护。比如一个用户对象可能有多种形态:
typescript复制type User = {
id: string;
name: string;
age?: number;
address?: {
street: string;
city: string;
};
};
使用类型别名后:
- 复杂类型有了清晰的名称
- 可以在多个地方复用这个定义
- 修改类型只需改一处
- 代码自文档化程度提高
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 联合类型的深度解析
2.1 基础语法与应用场景
联合类型使用|操作符连接多个类型,基本语法如下:
typescript复制let value: Type1 | Type2 | Type3;
常见应用场景包括:
- 处理多种输入类型:
typescript复制function formatInput(input: string | number): string {
return input.toString();
}
- API响应处理:
typescript复制type APIResponse = SuccessResponse | ErrorResponse;
- 状态管理:
typescript复制type LoadingState = "idle" | "loading" | "succeeded" | "failed";
2.2 类型缩小(Type Narrowing)
使用联合类型时,TypeScript会要求我们进行类型缩小,以确定当前处理的具体类型。常见的方法有:
- typeof类型守卫:
typescript复制function padLeft(value: string | number, padding: string): string {
if (typeof value === "number") {
return Array(value + 1).join(" ") + padding;
}
return value + padding;
}
- instanceof类型守卫:
typescript复制class Bird {
fly() {
console.log("Flying");
}
}
class Fish {
swim() {
console.log("Swimming");
}
}
function move(pet: Bird | Fish) {
if (pet instanceof Bird) {
pet.fly();
} else {
pet.swim();
}
}
- 自定义类型谓词:
typescript复制function isString(test: any): test is string {
return typeof test === "string";
}
function example(foo: any) {
if (isString(foo)) {
console.log("it's a string: " + foo);
} else {
console.log("it's something else");
}
}
2.3 联合类型的注意事项
- 只能访问共有成员:
typescript复制interface Bird {
fly(): void;
layEggs(): void;
}
interface Fish {
swim(): void;
layEggs(): void;
}
function getSmallPet(): Bird | Fish {
// ...
}
let pet = getSmallPet();
pet.layEggs(); // 可以,因为两个接口都有
// pet.fly(); // 错误,因为Fish没有fly方法
- 区分联合类型:
当联合类型中的类型有重叠时,可以使用判别属性来区分:
typescript复制type Square = {
kind: "square";
size: number;
};
type Rectangle = {
kind: "rectangle";
width: number;
height: number;
};
type Shape = Square | Rectangle;
function area(s: Shape) {
switch (s.kind) {
case "square":
return s.size * s.size;
case "rectangle":
return s.width * s.height;
}
}
3. 类型别名的进阶用法
3.1 基本语法与常见用途
类型别名使用type关键字定义,基本语法为:
typescript复制type AliasName = ExistingType;
常见用途包括:
- 简化复杂类型:
typescript复制type StringOrNumber = string | number;
- 定义函数类型:
typescript复制type ClickHandler = (event: MouseEvent) => void;
- 创建元组类型:
typescript复制type Data = [string, number, boolean];
3.2 泛型类型别名
类型别名也可以使用泛型,这使得它们更加灵活:
typescript复制type Container<T> = { value: T };
type Tree<T> = {
value: T;
left?: Tree<T>;
right?: Tree<T>;
};
3.3 类型别名与接口的区别
虽然类型别名和接口在很多情况下可以互换,但它们有一些关键区别:
| 特性 | 类型别名 | 接口 |
|---|---|---|
| 扩展方式 | 使用交叉类型(&) | 使用extends关键字 |
| 合并声明 | 不能合并 | 会自动合并 |
| 描述类型 | 可以描述任何类型 | 主要描述对象形状 |
| 实现类 | 不能直接实现 | 可以被类实现 |
选择建议:
- 如果需要扩展或实现,优先使用接口
- 如果需要联合类型、元组或其他复杂类型,使用类型别名
4. 联合类型与类型别名的协同应用
4.1 构建复杂的数据模型
在实际项目中,我们经常需要构建复杂的数据模型。联合类型和类型别名的组合可以很好地满足这种需求:
code复制
