TypeScript enum 和字面量联合类型怎么选?
简化版
enum 会生成运行时代码,适合需要运行时对象、反向映射或跨模块复用常量的场景;字面量联合类型只存在于类型层,更轻量,适合前端状态、配置值和 API 字段约束。现代前端更常用联合类型配合 as const。
详细版
示例:
enum Role {
Admin = 'admin',
User = 'user',
}
type Role2 = 'admin' | 'user';
选择思路:
- 只做类型约束:优先字面量联合类型。
- 需要运行时枚举对象:可以用
enum或as const对象。 - 对包体积敏感:联合类型更轻。
- 需要和后端常量对齐:字符串枚举或
as const对象更直观。
面试重点是:enum 有运行时,联合类型只在编译期。
完整版教学
一、先看它们是否会出现在运行时
TypeScript 类型大多会在编译后擦除。
字面量联合类型就是纯类型,运行时不存在。
普通 enum 不同,它会被编译成 JavaScript 对象。
type Status = 'pending' | 'success' | 'failed';
enum StatusEnum {
Pending = 'pending',
Success = 'success',
Failed = 'failed',
}
如果只需要限制变量只能取 3 个字符串,联合类型足够。
如果运行时要遍历所有可选项,enum 或常量对象才有实体可用。
二、数字 enum 有反向映射,字符串 enum 没有
数字枚举编译后会生成值到名字、名字到值的双向映射。 字符串枚举通常只有名字到值。 这也是数字枚举包体积和行为更复杂的原因。
enum Direction {
Up,
Down,
}
console.log(Direction.Up); // 0
console.log(Direction[0]); // "Up"
数字例子里 Up 是 0,Direction[0] 又能拿回 "Up"。
这在某些调试场景方便,但前端业务状态很多时候更希望值本身就是清晰字符串。
三、联合类型更适合前端状态字段
前端常见状态如按钮尺寸、主题、接口状态、组件 variant。 这些值通常来自字符串,运行时不需要一个枚举对象。 联合类型轻量、直观、和 JSON 字段天然一致。
type ButtonSize = 'small' | 'middle' | 'large';
function Button(props: { size: ButtonSize }) {
return props.size;
}
| 维度 | enum | 字面量联合类型 |
|---|---|---|
| 运行时代码 | 有 | 无 |
| 包体积 | 可能增加 | 无额外输出 |
| API 字符串字段 | 可用 | 很适合 |
| 遍历可选项 | 方便 | 需要配合数组或对象 |
很多 UI 组件库更倾向用联合类型描述 props。
四、as const 对象常用来替代 enum
如果既想要运行时对象,又想要类型推导,可以用 as const。
它能把对象值收窄成字面量类型。
再通过索引访问类型得到联合类型。
const Role = {
Admin: 'admin',
User: 'user',
} as const;
type Role = typeof Role[keyof typeof Role];
流程如下:
运行时对象 Role
-> as const 固定字面量
-> typeof Role 拿类型
-> keyof typeof Role 拿 key
-> 索引访问得到 value 联合
这套写法是现代前端工程里非常常见的 enum 替代方案。
五、const enum 要谨慎
const enum 会在编译时内联,通常不生成枚举对象。
它能减少运行时代码,但在 Babel、isolatedModules、库发布场景下容易遇到兼容问题。
如果配置链路不完全由 TypeScript 编译器处理,要特别小心。
const enum Code {
Ok = 200,
NotFound = 404,
}
const ok = Code.Ok; // 可能被内联成 200
记忆钩子:
enum像运行时常量表,联合类型像编译期取值范围。
项目内部可控时可以使用 const enum。
对外发布类型包或多工具链项目中,更推荐联合类型或 as const 对象。
六、工程选择建议
如果团队主要写前端业务和组件库,默认选择字面量联合类型。
如果需要运行时遍历选项,使用 as const 对象。
如果和后端或历史代码已经大量使用 enum,可以继续使用字符串 enum,但要理解运行时代码成本。
const STATUS_OPTIONS = ['pending', 'success', 'failed'] as const;
type Status = typeof STATUS_OPTIONS[number];
这个写法只有 1 份运行时数组,同时能得到类型。 适合表单选项、筛选项、状态列表。
七、常见误区与追问
- 误区:enum 只是类型,不会生成代码。 普通 enum 会生成运行时代码,联合类型才会被擦除。
- 误区:数字 enum 和字符串 enum 行为完全一样。 数字 enum 有反向映射,字符串 enum 通常没有。
- 误区:const enum 永远更好。 它依赖编译链路,库发布和 Babel 场景要谨慎。
- 追问:为什么前端组件 props 常用联合类型? 它轻量、直观、无运行时代码,能直接约束字符串取值。
- 追问:如何既有运行时对象又有类型? 使用
as const对象或数组,再通过typeof推导联合类型。 - 追问:enum 适合什么场景? 适合需要运行时枚举对象、跨模块常量表或历史系统统一常量的场景。
八、加强记忆
选型时先问一句:运行时需不需要这个常量表。需要就考虑 enum 或 as const,不需要就用联合类型。现代前端常用“as const 数据源 + 推导联合类型”的方式,同时兼顾运行时和类型层。