Skip to main content

TypeScript Enum 还有用吗?

· 4 min read

TS 5.0+ 起 enum 已经从"首选"变成"备选"——能 as const 对象就别 enum。

  • 三种 enum 类型:数字(自动递增 + 反向映射)、字符串、异构(不推荐)
  • 数字 enum 的坑:编译后是 JS 对象,无法 tree-shake,反向映射易踩坑
  • 字符串 enum:tree-shake 友好,但运行时仍有开销
  • const enum:跟 isolatedModules 冲突,TS 5.0 起不再推荐
  • 首选替代as const 对象 + 联合类型,零运行时开销

enum 在 2026 年的地位

TS 团队在 5.0 之后基本不再推 enum。原因是它把"枚举"这个纯类型层级的概念强行塞进了运行时——数字 enum 编译后是带反向映射的 JS 对象,字符串 enum 编译后是一堆赋值语句。两者都让 tree-shaking 失效,bundle 里总会带上整个 enum 表。

现代推荐:用 as const 对象 + 联合类型替代 enum。下面会讲清 enum 的细节,再讲为什么替代品更好。


三种 enum 类型

// 数字 enum:自动递增 + 反向映射
enum Direction {
Up, // 0
Down, // 1
Left, // 2
Right, // 3
}

// 字符串 enum:每个成员必须显式赋值
enum Status {
Active = 'ACTIVE',
Inactive = 'INACTIVE',
}

// 异构 enum:数字和字符串混着来
enum Mixed {
No = 0,
Yes = 'YES',
}

注意:异构 enum 在 TypeScript 官方手册 里就被标了"几乎从不需要",别用


数字 enum 与反向映射

数字 enum 默认从 0 开始递增,可以手动赋初值打断递增。但有 两个坑

坑 1:手动赋值较小会被覆盖

enum Days {
Sun = 3,
Mon = 1,
Tue, // 2
// ...
Wed, // 3(和 Sun 撞了)
}
Days[3] === 'Sun'; // false
Days[3] === 'Wed'; // true(后定义的覆盖)

坑 2:反向映射是个 JS 对象

数字 enum 编译后是双向键值对:

var Days;
(function (Days) {
Days[(Days['Sun'] = 0)] = 'Sun';
Days[(Days['Mon'] = 1)] = 'Mon';
})(Days || (Days = {}));

这就导致 for...in Days 会同时遍历出 key 和 value 两个版本:

for (const k in Days) {
console.log(k); // '0', '1', ..., 'Sun', 'Mon', ...
}

最佳实践:遍历数字 enum 时用 Object.keys(Days).filter(k => isNaN(Number(k))) 过滤出字符串 key。


字符串 enum:tree-shake 友好但仍有运行时开销

enum Direction {
Up = 'UP',
Down = 'DOWN',
Left = 'LEFT',
Right = 'RIGHT',
}

字符串 enum 没有反向映射——只生成 Direction['Up'] = 'UP' 这种单向赋值。tree-shaker 能识别出哪些引用没用到,把没用的成员删掉。

但它仍然是运行时对象,没 as const 那么彻底。


const enum 跟 isolatedModules 冲突

const enum 的本意是编译时直接内联,省掉运行时对象:

const enum Direction {
Up = 'UP',
Down = 'DOWN',
}
const d = Direction.Up; // 编译后 const d = 'UP';

isolatedModules(Babel、esbuild、SWC 强制开启)要求每个文件能独立编译,没法跨文件做内联替换。开了 isolatedModules 的项目 const enum 直接报错:

error TS2748: Cannot access 'X' from another module without 'isolatedModules'
or 'preserveConstEnums' flag

TS 5.0 起官方不再推荐 const enum,因为现代构建工具链都默认开 isolatedModules


首选替代:as const 对象 + 联合类型

const Direction = {
Up: 'UP',
Down: 'DOWN',
Left: 'LEFT',
Right: 'RIGHT',
} as const;

type Direction = typeof Direction[keyof typeof Direction];
// type Direction = 'UP' | 'DOWN' | 'LEFT' | 'RIGHT'

收益:

  • 零运行时as const 对象能完全 tree-shake,没用到的成员直接消失
  • 类型安全type Direction 联合类型保证参数只能是 4 个字符串之一
  • IDE 友好:跳转、重构、查找引用都正常
  • 跨工具兼容:Babel、SWC、esbuild 都能处理,不用管 isolatedModules
维度enumas const 对象
运行时开销有(数字 enum 是双向对象)无(可 tree-shake)
类型保护自动生成联合类型需要手写 typeof
isolatedModules数字 enum OK,const enum 不行完全兼容
Tree-shaking数字 enum 失效完全支持
反向映射仅数字 enum需要手写 helper

提示:唯一还应该用 enum 的场景是 声明合并(declaration merging) ——enum 允许在不同文件里给同一个 enum 加成员,as const 对象做不到。


References

  1. TypeScript Handbook: Enums —— 官方手册
  2. TypeScript 5.0 Release Notes —— const enum 弃用说明
  3. Should I use enums in TypeScript? - stackoverflow —— 社区共识