TypeScript: TypeScript 枚举 (enum)
最后更新:2026-08-26
枚举(enum)是 TypeScript 少数几个在运行时有表现的类型特性——它既是一种类型,也生成实际的 JavaScript 对象。
1. 数字枚举
(1) 基本语法
TYPESCRIPT
enum Direction {
Up, // 0(自动从0递增)
Down, // 1
Left, // 2
Right // 3
}
let dir: Direction = Direction.Up;
console.log(dir); // 0
console.log(Direction[0]); // "Up"(反向映射)
(2) 自定义初始值
TYPESCRIPT
enum Status {
Active = 1, // 1
Inactive, // 2(自动递增)
Pending // 3
}
enum HttpStatus {
OK = 200,
Moved = 301,
BadRequest = 400,
NotFound = 404,
ServerError = 500
}
let code: HttpStatus = HttpStatus.OK;
console.log(code); // 200
(3) 数字枚举的反向映射
数字枚举编译后会生成双向映射对象——既可以通过名取值,也可以通过值取名:
TYPESCRIPT
enum Role {
Admin = 0,
Editor = 1,
Viewer = 2
}
// 正向映射:名 → 值
console.log(Role.Admin); // 0
// 反向映射:值 → 名
console.log(Role[0]); // "Admin"
console.log(Role[1]); // "Editor"
编译后生成的 JavaScript:
JAVASCRIPT
var Role;
(function (Role) {
Role[Role["Admin"] = 0] = "Admin";
Role[Role["Editor"] = 1] = "Editor";
Role[Role["Viewer"] = 2] = "Viewer";
})(Role || (Role = {}));
2. 字符串枚举
(1) 基本语法
字符串枚举的每个成员必须显式赋值——没有自动递增:
TYPESCRIPT
enum EventType {
Click = "click",
Change = "change",
Submit = "submit",
Focus = "focus"
}
let event: EventType = EventType.Click;
console.log(event); // "click"
(2) 字符串枚举没有反向映射
TYPESCRIPT
enum Color {
Red = "RED",
Green = "GREEN",
Blue = "BLUE"
}
console.log(Color.Red); // "RED" ✅ 正向映射
// console.log(Color["RED"]); // undefined ❌ 字符串枚举没有反向映射
💡 原因: 数字枚举的值是数字,可以做对象键;字符串枚举的值是字符串,和对象键冲突,所以无法实现反向映射。
▶ 示例:用枚举定义订单状态
TYPESCRIPT
enum OrderStatus {
Pending = "PENDING",
Processing = "PROCESSING",
Shipped = "SHIPPED",
Delivered = "DELIVERED",
Cancelled = "CANCELLED"
}
function getStatusLabel(status: OrderStatus): string {
switch (status) {
case OrderStatus.Pending: return "待处理";
case OrderStatus.Processing: return "处理中";
case OrderStatus.Shipped: return "已发货";
case OrderStatus.Delivered: return "已送达";
case OrderStatus.Cancelled: return "已取消";
}
}
let currentStatus: OrderStatus = OrderStatus.Processing;
console.log(`当前状态:${getStatusLabel(currentStatus)}`);
console.log(`状态码:${currentStatus}`);
输出:
TEXT
📖 仅展示
当前状态:处理中
状态码:PROCESSING
3. 异构枚举
枚举可以混合数字和字符串值——但不推荐:
TYPESCRIPT
enum Mixed {
No = 0,
Yes = "YES"
}
⚠️ 不推荐: 异构枚举容易引起混淆,TypeScript 官方文档也建议避免使用。实际开发中,要么全用数字枚举,要么全用字符串枚举。
4. const 枚举
const enum 在编译时被内联替换——不生成 JavaScript 对象,性能更好但功能受限:
(1) 基本语法
TYPESCRIPT
const enum Color {
Red = "RED",
Green = "GREEN",
Blue = "BLUE"
}
let c = Color.Red;
// 编译后:let c = "RED"(直接内联替换,没有枚举对象)
(2) const 枚举的限制
TYPESCRIPT
const enum Direction {
Up = "UP",
Down = "DOWN"
}
// ❌ const 枚举不能使用反向映射
// console.log(Direction[0]);
// ❌ const 枚举不能在运行时遍历
// for (let d in Direction) { }
// ✅ 只能做简单的值引用
let dir = Direction.Up; // 编译为 let dir = "UP"
(3) preserveConstEnums 选项
如果需要 const 枚举不内联但保留枚举对象,开启 preserveConstEnums: true:
TYPESCRIPT
// tsconfig.json
// "preserveConstEnums": true
const enum Status {
Active = 1
}
let s = Status.Active;
// 编译后:枚举对象仍然生成(但引用也会内联)
5. 枚举的运行时与类型安全
(1) 枚举作为类型
TYPESCRIPT
enum Role {
Admin,
Editor,
Viewer
}
function checkAccess(role: Role): boolean {
return role === Role.Admin || role === Role.Editor;
}
checkAccess(Role.Admin); // ✅
checkAccess(0); // ✅ 数字枚举接受数字值(但 ⚠️ 不推荐)
// checkAccess(99); // ✅ 编译通过!任何数字都能通过——这是数字枚举的漏洞
🔥 数字枚举的陷阱: 数字枚举类型的变量可以接受任何数字——
let r: Role = 99 是合法的。这是因为数字枚举的设计考虑了位标志(bit flags)。如果需要严格的值约束,用字符串枚举或字面量联合类型。
(2) 字符串枚举更安全
TYPESCRIPT
enum Role {
Admin = "ADMIN",
Editor = "EDITOR",
Viewer = "VIEWER"
}
let r: Role = Role.Admin; // ✅
// r = "ADMIN"; // ❌ 字符串枚举不接受普通字符串
// r = "SUPERADMIN"; // ❌ 只接受枚举成员
6. 枚举 vs 字面量联合类型
| 特性 | 枚举 | 字面量联合类型 |
|---|---|---|
| 运行时代码 | 有(生成对象) | 无(纯类型) |
| 反向映射 | 数字枚举有 | 无 |
| 遍历所有值 | 可以 | 不行 |
| 值约束 | 数字枚举较宽松 | 严格 |
| 代码提示 | 枚举成员自动提示 | 联合值自动提示 |
| bundle 大小 | 增加代码体积 | 零体积 |
| 和 JS 互操作 | 枚举值是自定义对象 | 原生字符串/数字 |
(1) 用枚举的场景
- 需要反向映射(值 → 名)
- 需要在运行时遍历所有值
- 需要枚举作为运行时对象使用(如 API 常量映射)
(2) 用字面量联合类型的场景(推荐优先)
- 只需要类型约束
- 需要和原生 JavaScript 字符串/数字互操作
- 追求最小 bundle 体积
- 不需要运行时行为
▶ 示例:两种方式对比
TYPESCRIPT
// 方式一:枚举
enum Direction1 {
Up = "UP",
Down = "DOWN",
Left = "LEFT",
Right = "RIGHT"
}
// 方式二:字面量联合类型(推荐)
type Direction2 = "UP" | "DOWN" | "LEFT" | "RIGHT";
// 两者在类型约束上等价
function move1(dir: Direction1): void { console.log(dir); }
function move2(dir: Direction2): void { console.log(dir); }
move1(Direction1.Up); // ✅ "UP"
move2("UP"); // ✅ 直接用字符串,更简单
// 但枚举可以遍历
console.log(Object.values(Direction1));
// ["UP", "DOWN", "LEFT", "RIGHT"]
// 字面量联合类型无法遍历(纯编译时类型)
❓ 常见问题
Q 到底该不该用枚举?
A TypeScript 社区分两派——一派认为枚举是 TypeScript 的特色功能应该用,另一派认为字面量联合类型更轻量更推荐。实际建议:如果只需要类型约束(大多数情况),用字面量联合类型;如果需要运行时行为(遍历、反向映射),用枚举。
Q 数字枚举为什么能接受任意数字?
A 这是 TypeScript 的设计决策——数字枚举支持位标志(
enum Perm { Read = 1, Write = 2, Execute = 4 }),位组合 Read | Write = 3 是合法的但不在枚举定义中。所以数字枚举类型放宽了约束。如果不想要这个行为,用字符串枚举或字面量联合类型。Q const 枚举和普通枚举有什么区别?
A const 枚举在编译时内联替换——
Color.Red 直接变成 "RED",不生成枚举对象。好处是 bundle 更小、运行时更快;缺点是不能反向映射、不能遍历、不能在动态场景使用。优先用 const 枚举(除非需要运行时特性)。Q 枚举可以和接口/类型别名组合吗?
A 可以。枚举的值可以作为接口的属性类型——
interface Config { role: Role }。枚举成员也可以作为联合类型成员——type Mixed = Role.Admin | "superadmin"。枚举是类型和值的统一体,用法灵活。📖 小节
- 数字枚举从0自动递增,支持反向映射;字符串枚举必须显式赋值,无反向映射
- 异构枚举(混合数字和字符串)不推荐使用
- const 枚举编译时内联替换,零运行时开销,但功能受限
- 数字枚举类型能接受任意数字(设计缺陷),字符串枚举更严格
- 大多数场景推荐用字面量联合类型替代枚举——更轻量、更安全、零体积
- 需要反向映射或遍历所有值时才用枚举
📝 作业
- 基础题(难度⭐):定义一个字符串枚举
Season(Spring/Summer/Autumn/Winter),写一个函数根据季节返回对应的月份范围描述。 - 进阶题(难度⭐⭐):用 const enum 定义 HTTP 方法(GET/POST/PUT/DELETE),然后定义一个
Request接口包含 method 属性和 url 属性。创建几个请求对象验证类型约束。 - 挑战题(难度⭐⭐⭐):用字面量联合类型替代枚举,实现一个"权限系统":定义
Permission = "read" | "write" | "execute" | "admin",然后实现hasPermission(userPerms: Permission[], required: Permission): boolean函数,检查用户是否有指定权限(admin 拥有所有权限)。