枚举的本质与编译结果
枚举的本质与编译结果
一句话概括
enum 是 TypeScript 少数几个会”活到运行时”的语法:数字枚举变双向映射对象,const enum 直接蒸发不留痕迹,做错选择要么多打几 KB,要么 isolatedModules 炸给你看。
核心知识点
1. 数字枚举 → 双向映射对象
1
2
3
4
5
6
7
8
enum Direction { Up, Down, Left, Right }
// 编译后的 JS(简化理解):
// { 0: "Up", 1: "Down", 2: "Left", 3: "Right",
// Up: 0, Down: 1, Left: 2, Right: 3 }
console.log(Direction.Up); // 0(名 → 值)
console.log(Direction[0]); // "Up"(值 → 名,反向映射!)
反向映射的代价:这个对象不能被 tree-shake(立即执行函数有副作用),即使你只用了其中一个值。
2. 字符串枚举 → 单向映射,无反向
1
2
3
4
5
6
7
8
enum Status {
Active = "ACTIVE",
Inactive = "INACTIVE",
}
// 编译后:{ Active: "ACTIVE", Inactive: "INACTIVE" }
console.log(Status.Active); // "ACTIVE" ✅
console.log(Status["ACTIVE"]); // undefined ❌ 没有反向映射
字符串枚举类型更严格——不能把任意字符串赋给枚举类型,编译产物也更干净。
3. const enum → 编译时蒸发
1
2
3
const enum Color { Red, Green, Blue }
const c = Color.Red;
// 编译后:const c = 0; ← 直接内联,没有 Color 对象!
零运行时开销,完美 tree-shake,但——
1
2
3
// ❌ isolatedModules: true 时容易炸
// Babel/esbuild/swc 没法跨文件内联 const enum
export const x = MyConstEnum.Foo; // 可能报错
4. 数字枚举的「不安全」特性
1
2
3
enum Status { Active, Inactive }
let s: Status = 999; // ✅ 数字枚举允许任意数字——历史遗留设计
// 字符串枚举没有这个问题
5. as const 对象:现代替代方案(推荐)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const UserRole = {
Admin: "ADMIN",
Editor: "EDITOR",
Viewer: "VIEWER",
} as const;
type UserRole = typeof UserRole[keyof typeof UserRole];
// "ADMIN" | "EDITOR" | "VIEWER"
// 优势:
// ✅ 零运行时开销(就是普通对象,天然 tree-shakeable)
// ✅ isolatedModules 友好
// ✅ 值和类型同源,新增值不用改两个地方
// ✅ 可以用 Object.keys/values/entries 遍历
「其实你每天都在用」
- HTTP 状态码:
enum HttpCode { OK = 200, NotFound = 404 },利用反向映射HttpCode[404]→"NotFound"打日志 - Redux Action Type:
enum ActionType { FETCH_USER = "FETCH_USER" } - 位运算权限:
const enum Perm { Read = 1, Write = 2, Admin = 4 },const enum 这里最合适,编译后就是纯数字运算 - 配置常量:如主题
"light" | "dark"、环境"dev" | "staging" | "prod"——这些场景用 as const 对象最舒服 - 组件 props 的可选值:
type Size = "small" | "medium" | "large"→ 直接用联合类型,不需要 enum
常见误解(FAQ)
❌ 误区 1:const enum 总是优于 enum
const enum 在 isolatedModules: true 下可能直接报错——现在几乎所有项目都在用 Babel/esbuild/swc 做编译,这个坑非常常见。如果你不确定构建工具链是否支持,用 as const 对象最安全。
❌ 误区 2:enum 的值必须有意义
数字枚举的值 0, 1, 2 在序列化或日志里毫无意义。如果你需要存数据库或跨系统传输,字符串枚举或 as const 对象是更好的选择。
❌ 误区 3:TS 枚举和 Java/C# 枚举一样
Java 的 enum 是一等公民(可以有方法、字段、构造器),TypeScript 的 enum 只是语法糖,编译后就是普通对象。不要按 Java 的习惯来用 TS enum。
❌ 误区 4:联合类型能完全替代 enum
大部分场景能,但需要反向映射(数字枚举的 Direction[0] → "Up")时不行,运行时遍历键值时 as const 对象更方便但更”重”。选型看场景,不要一刀切。
一句话总结
能联合类型就联合类型,需要运行时存在就用 as const 对象,实在需要反向映射再考虑 enum——别为了一个常量定义往包里多打 500 字节。
本文由作者按照 CC BY 4.0 进行授权