很多人眼里的 enum,大概是这样:
enum Status {
Pending,
Approved,
Rejected
}
看起来人畜无害,对吧?但幕后真相是:TypeScript 会把它编译成一坨真实存在的 JavaScript 对象,类似这样:
"use strict";
var Status;
(function (Status) {
Status[Status["Pending"] = 0] = "Pending";
Status[Status["Approved"] = 1] = "Approved";
Status[Status["Rejected"] = 2] = "Rejected";
})(Status || (Status = {}));
你没看错:一个“整洁的小枚举”,变成了双向映射对象。所以你能这么玩:
Status.Approved; // 1
Status[1]; // "Approved"
为什么要这样?因为 TypeScript 的 enum 不是纯编译期产物——它会在JavaScript运行时真实存在。
这很怪。也很容易让人困惑。更容易让同事看着代码小声嘀咕一句:“这到底是什么鬼?”
现实一点:你根本不需要“运行时枚举”
我们说实话:你上一次真的需要 enum 的反向映射(Status[1] => “Approved”)是什么时候?
大多数情况下:从来没有。
你需要的通常只是:一组已知值——状态、角色、方向、颜色……你要的是“限定范围”,不是“运行时对象”。
那就别给自己加戏。用现代 TS 的方式:字面量联合类型或 as const 对象。
例如这样:
const Status = {
Pending: 'Pending',
Approved: 'Approved',
Rejected: 'Rejected'
} as const;
type Status = typeof Status[keyof typeof Status];
你得到的是:
✅ 强类型约束
✅ 自动补全
✅ 没有诡异的运行时双向映射
✅ 输出简单可预测
最爽的是:这套模式几乎不会带来额外的运行时负担——类型信息是 TS 的魔法,编译后不会给你塞一坨“神秘对象”。
隐形成本:Enum 会“悄悄把项目带歪”
enum 最恐怖的地方不是它丑,而是它出问题时很阴,尤其在大代码库里。
1. 循环依赖:你会突然遇到 undefined 的惊喜
因为 enum 是真实的 JS 对象,所以当它在多个文件间被引入、再被引入,很容易就把你拖进循环依赖。
于是某天你会遇到:“咦?EnumName 怎么突然是 undefined?”然后你开始排查半天,最后发现:是 import 顺序在搞你。
2. JSON / API 地狱:你到底传的是数字还是字符串?
把 enum 值丢到 JSON 里时,你会发现它可能是数字,也可能是字符串——取决于你写的是 numeric enum 还是 string enum。
下一秒,你的后端同事就会发来一句消息:
“为什么 status = 0,不是 ‘Pending’?你还好吗?”
然后你解释半天,最后大家一起浪费生命。
3. 代码不一致:数字枚举 + 字符串枚举混用 = 进入结界
你一旦在项目里混用 numeric enum 和 string enum,恭喜你:你踏进了一个“看似类型安全、实际越来越玄学”的区域。
你以为 TS 会拯救你。结果 TS 只是在旁边看戏。
更简单的真相:你需要的是 Literal Types
enum 让人上头,是因为它看起来“结构化”。但现代 TypeScript 的类型系统已经能让你结构化,而且不用背那一堆包袱。
比如直接写:
type Role = 'Admin' | 'User' | 'Guest';
function getAccessLevel(role: Role) {
switch (role) {
case 'Admin':
return 'Full';
case 'User':
return 'Limited';
default:
return 'None';
}
}
没有运行时成本。没有循环依赖。没有“为什么变成数字”的鬼故事。只有清爽、可读、可维护。
你不需要 enums。你需要的是清晰。
我的经验法则
只有在满足这两个条件时,我才会考虑 enum:
- 你确实需要运行时查表 / 映射(而不是单纯限定值)
- 你能控制系统两端(前端 + 后端/服务端)并且约定一致
否则?扔掉。
如果你的“枚举”不需要在运行时存在,那就别让它存在。用 as const 或字面量联合,你照样有类型安全,但不会引入混乱。
最后
enum 本来是来“带来秩序”的。结果它带来的更像是:官僚主义。
它就像 TypeScript 里的“办公室中层”:总是不请自来,增加流程,让每个人都困惑一点点,然后还特别难删。
TypeScript 已经进化了。我们的习惯也该进化。
下次你想写 enum 的时候,停一秒,问问自己:“我是真的需要它,还是只是因为它看起来顺手?”
如果是后者——选 as const 或字面量联合类型。未来的你(以及你的队友)都会感谢你。
技术选型总是在权衡,而了解其潜在的性能开销与维护成本至关重要。如果你想探索更多关于现代前端开发的最佳实践和避坑指南,欢迎在云栈社区与其他开发者交流讨论。