文章

枚举的本质与编译结果

枚举的本质与编译结果

一句话概括

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 遍历

「其实你每天都在用」

  1. HTTP 状态码enum HttpCode { OK = 200, NotFound = 404 },利用反向映射 HttpCode[404]"NotFound" 打日志
  2. Redux Action Typeenum ActionType { FETCH_USER = "FETCH_USER" }
  3. 位运算权限const enum Perm { Read = 1, Write = 2, Admin = 4 },const enum 这里最合适,编译后就是纯数字运算
  4. 配置常量:如主题 "light" | "dark"、环境 "dev" | "staging" | "prod"——这些场景用 as const 对象最舒服
  5. 组件 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 进行授权