TypeScript 枚举:enum 类型与数字枚举
TypeScript 的 enum(枚举)是一种为一组相关常量赋予语义化名称的方式。它让代码更易读、更安全,尤其适用于表示状态、选项或分类等有限集合的场景。其中,数字枚举是最基础也最常用的类型。
创建一个基本的数字枚举
定义一个数字枚举非常简单:
enum Direction {
Up,
Down,
Left,
Right
}
在这个例子中,Direction 是一个枚举类型。TypeScript 自动为每个成员分配递增的数字值,从 0 开始:
Direction.Up的值是0Direction.Down的值是1Direction.Left的值是2Direction.Right的值是3
你可以像使用普通变量一样使用这些枚举成员:
let currentDirection = Direction.Up;
if (currentDirection === Direction.Down) {
// 执行向下逻辑
}
显式指定数字值
虽然 TypeScript 默认从 0 开始自增,但你可以显式设置任意成员的数值:
enum HttpStatus {
OK = 200,
NotFound = 404,
InternalServerError = 500
}
此时:
HttpStatus.OK是200HttpStatus.NotFound是404HttpStatus.InternalServerError是500
如果你只给第一个成员赋值,后续成员仍会自动递增:
enum Priority {
Low = 1,
Medium, // 自动为 2
High // 自动为 3
}
反向映射:从数字获取名称
数字枚举的一个独特特性是支持反向映射。这意味着你不仅可以用名称获取数字,还能用数字反查名称:
enum Color {
Red,
Green,
Blue
}
console.log(Color[0]); // 输出 "Red"
console.log(Color["Red"]); // 输出 0
这种双向映射在调试或日志记录时非常有用。但注意:只有数字枚举才有反向映射,字符串枚举或其他变体没有此功能。
使用枚举提升代码可读性
假设你在写一个游戏角色移动逻辑。不用枚举时,代码可能像这样:
function move(direction: number) {
if (direction === 0) {
// 向上
} else if (direction === 1) {
// 向下
}
}
数字 0、1 的含义不明确。改用枚举后:
enum MoveDirection {
Up,
Down,
Left,
Right
}
function move(direction: MoveDirection) {
if (direction === MoveDirection.Up) {
// 向上
} else if (direction === MoveDirection.Down) {
// 向下
}
}
现在,代码意图一目了然,且编译器能帮你防止传入非法值(如 move(99) 会被 TypeScript 报错)。
编译后的 JavaScript 行为
TypeScript 的枚举在编译成 JavaScript 时会生成额外的运行时对象。例如:
enum Status {
Pending,
Success,
Error
}
编译后变成:
var Status;
(function (Status) {
Status[Status["Pending"] = 0] = "Pending";
Status[Status["Success"] = 1] = "Success";
Status[Status["Error"] = 2] = "Error";
})(Status || (Status = {}));
这个结构正是实现反向映射的关键:Status[0] 返回 "Pending",而 Status["Pending"] 返回 0。
数字枚举的潜在陷阱
尽管方便,数字枚举也有需要注意的地方:
-
隐式类型宽松:由于数字枚举本质是数字,TypeScript 允许将任意
number赋值给枚举类型变量(除非开启--strict模式)。例如:let state: Direction = 99; // 不报错!这可能导致运行时错误。为避免此问题,可考虑使用字符串枚举或联合类型。
-
值可能冲突:如果你手动赋值不当,可能导致多个成员拥有相同数字:
enum BadExample { A = 1, B = 1 // 合法,但危险 }此时
BadExample.A === BadExample.B为true,容易引发逻辑混乱。
替代方案:使用 const enum(常量枚举)
如果你希望完全消除运行时开销,可以使用 const enum:
const enum FeatureFlag {
DarkMode = 1,
AutoSave = 2
}
编译时,TypeScript 会直接将引用替换为字面量值:
if (user.flags & FeatureFlag.DarkMode) { ... }
// 编译后变为:
if (user.flags & 1) { ... }
但注意:const enum 不能被其他文件通过模块导入后动态访问(如 Object.keys(FeatureFlag) 会失败),仅适合纯编译期替换的场景。
最佳实践建议
-
优先考虑字符串枚举:如果你不需要数字运算或反向映射,字符串枚举更安全、更清晰:
enum UserRole { Admin = "ADMIN", Editor = "EDITOR", Viewer = "VIEWER" } -
明确赋值起始值:如果使用数字枚举,建议显式设置第一个值(如
Start = 1),避免意外从0开始导致逻辑歧义。 -
避免混合赋值:不要随意在中间成员插入显式数字,以免破坏自动递增逻辑。
-
启用 strict 模式:在
tsconfig.json中开启"strict": true,可防止将任意数字赋给枚举类型。
{
"compilerOptions": {
"strict": true
}
}
常见使用场景对照表
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| HTTP 状态码 | 数字枚举 | 值固定且为数字,便于比较 |
| 用户角色权限 | 字符串枚举 | 名称比数字更具语义,避免冲突 |
| 游戏方向控制 | 数字枚举(带显式赋值) | 需要简洁性和性能,值范围小 |
| 功能开关标志 | const enum 或联合类型 |
无需运行时对象,追求零开销 |
| 状态机状态 | 字符串字面量联合类型 | 更灵活,无运行时负担 |
当你需要一组固定的、命名的常量,并且这些常量天然对应数字(如状态码、位掩码、索引等),数字枚举是一个简洁有效的选择。但务必清楚其行为边界,避免因隐式转换引入隐患。

暂无评论,快来抢沙发吧!