TypeScript 枚举
枚举是 TypeScript 为数不多的、JavaScript 中没有的特性。它让你用一组有名字的常量来代替散落在代码各处的「数字」或「字符串」。
先看一段没有枚举的代码:
function getOrderStatus(status: number): string {
if (status === 0) return '待支付';
if (status === 1) return '已支付';
if (status === 2) return '已发货';
if (status === 3) return '已完成';
return '未知状态';
}
// 调用时:0 是什么意思?1 又是什么意思?
getOrderStatus(0);
getOrderStatus(1);
这段代码有两个问题:
- 可读性差:
0、1、2这些数字含义不明。 - 容易出错:不小心传了
4也能运行,但逻辑是错的。
用枚举改造后,这些问题迎刃而解:
enum OrderStatus {
Pending = 0, // 待支付
Paid = 1, // 已支付
Shipped = 2, // 已发货
Completed = 3, // 已完成
}
function getOrderStatus(status: OrderStatus): string {
switch (status) {
case OrderStatus.Pending: return '待支付';
case OrderStatus.Paid: return '已支付';
case OrderStatus.Shipped: return '已发货';
case OrderStatus.Completed: return '已完成';
default: return '未知状态';
}
}
// 调用:语义清晰,且只能传入枚举值
getOrderStatus(OrderStatus.Paid); // ✅ 清晰!
getOrderStatus(4); // ❌ 类型错误
枚举类型
数字枚举
数字枚举是默认类型,如果不给值,TypeScript 会自动从 0 开始递增:
// 不写值,默认从 0 开始
enum Direction {
Up, // 0
Down, // 1
Left, // 2
Right, // 3
}
console.log(Direction.Up); // 0
console.log(Direction.Down); // 1
你也可以手动赋值:
enum Status {
Pending = 1,
Paid = 2,
Shipped = 3,
Completed = 4,
}
部分赋值时,后面的自动递增:
enum Status {
Pending = 1, // 1
Paid, // 2(自动递增)
Shipped, // 3
Completed = 10, // 10
Archived, // 11
}
反向映射
数字枚举支持反向映射——可以通过值反查出键名:
enum Direction {
Up = 1,
Down,
Left,
Right,
}
console.log(Direction[1]); // 'Up'
console.log(Direction.Down); // 2
console.log(Direction[2]); // 'Down'
这个特性是因为数字枚举编译后生成了对象,但字符串枚举没有反向映射,这个后面会讲。
字符串枚举
字符串枚举的每个值必须用字符串字面量赋值,没有自动递增:
enum Direction {
Up = 'UP',
Down = 'DOWN',
Left = 'LEFT',
Right = 'RIGHT',
}
console.log(Direction.Up); // 'UP'
什么时候用字符串枚举?
| 场景 | 推荐 |
|---|---|
API 返回的状态码('success' / 'error') |
✅ 字符串枚举 |
| 需要日志可读性(看到的是英文单词而非数字) | ✅ 字符串枚举 |
| 后端约定的是数字状态值 | ✅ 数字枚举 |
| 需要做位运算或按位组合 | ✅ 数字枚举 |
字符串枚举的优势是调试时日志更友好,但缺点是没有反向映射:
enum Direction {
Up = 'UP',
Down = 'DOWN',
}
console.log(Direction.Up); // 'UP'
console.log(Direction['UP']); // ❌ 报错,字符串枚举不支持反向映射
异构枚举(不推荐)
枚举可以混用数字和字符串,但强烈不推荐:
enum Mixed {
Up = 1,
Down = 'DOWN', // 可以,但很怪
}
实用建议:保持统一,要么全数字,要么全字符串。
常量枚举
用 const 声明枚举,编译后会被完全移除,所有引用直接替换成具体的值:
const enum Direction {
Up = 'UP',
Down = 'DOWN',
}
console.log(Direction.Up);
// 编译后直接变成:
// console.log('UP');
为什么要用常量枚举?
- 减少编译后的代码体积
- 没有运行时对象,性能更优
注意事项:常量枚举只能使用字面量表达式,不能使用计算值。
枚举的实际使用场景
场景一:状态管理
enum OrderStatus {
Pending = 'pending',
Paid = 'paid',
Shipped = 'shipped',
Completed = 'completed',
}
// 根据状态渲染不同的 UI
function renderStatus(status: OrderStatus): string {
const map = {
[OrderStatus.Pending]: '🟡 待支付',
[OrderStatus.Paid]: '🔵 已支付',
[OrderStatus.Shipped]: '🟣 已发货',
[OrderStatus.Completed]: '🟢 已完成',
};
return map[status] || '未知';
}
renderStatus(OrderStatus.Paid); // '🔵 已支付'
场景二:定义固定的选项列表
enum UserRole {
Admin = 'admin',
Editor = 'editor',
Viewer = 'viewer',
}
function checkPermission(role: UserRole): boolean {
return role === UserRole.Admin;
}
场景三:与类型注解配合
enum HttpMethod {
GET = 'GET',
POST = 'POST',
PUT = 'PUT',
DELETE = 'DELETE',
}
function request(url: string, method: HttpMethod): void {
// 实现请求逻辑
}
request('/api/users', HttpMethod.GET); // ✅
request('/api/users', 'PATCH'); // ❌ 'PATCH' 不在枚举中