阮一峰《TypeScript 教程》学习笔记——Enum 类型
1. 一段话总结
Enum(枚举) 是 TypeScript 特有的数据结构与类型(JavaScript 无此类型),核心作用是将相关常量聚合管理,兼具类型语义与运行时值(编译后转为 JavaScript 对象,const enum 编译后会替换为常量值以优化性能);主要分为数值Enum(默认从0递增,支持显式赋值、计算式,存在反向映射)与字符串Enum(需显式赋值,不支持表达式,无反向映射);支持同名Enum自动合并(需满足“仅一个首成员可省略初始值、无同名成员、同const/非const”);可通过keyof typeof Enum获取成员名联合类型;其功能可部分被as const断言(原生对象+只读)或联合类型替代,官方建议谨慎使用以避免冗余编译产物。
2. 思维导图

3. 详细总结
一、Enum 类型基础
-
核心定位
Enum 是 TypeScript 新增的“类型+值”双属性结构,JavaScript 无此类型:- 类型语义:用于约束变量取值范围(如
let c:Color = Color.Red); - 运行时值:编译后转为 JavaScript 对象(非const enum),如:
// 编译前(非const enum) enum Color { Red, Green } // 编译后 let Color = { Red: 0, Green: 1 }; - const enum 优化:加
const修饰后,编译时会将成员直接替换为对应值(无对象生成),提升性能:// 编译前(const enum) const enum Color { Red, Green } const x = Color.Red; // 编译后 const x = 0 /* Color.Red */;
- 类型语义:用于约束变量取值范围(如
-
适用场景
当“成员名的语义价值”高于“具体值”时(如状态标识、操作类型),示例:enum Operator { ADD, SUB, MUL, DIV } // 四则运算标识,值无关紧要 -
替代方案
因 Enum 会生成冗余编译产物,官方建议优先使用原生方案:替代方案 语法示例 优势 as const断言const Bar = { A:0, B:1 } as const原生JS对象,只读不可改 联合类型 `type Dir = ‘Up’ ‘Down’`
二、Enum 成员值规则
Enum 成员值分为“数值型”与“字符串型”,规则差异显著,具体对比如下:
| 对比维度 | 数值Enum | 字符串Enum |
|---|---|---|
| 默认赋值 | 未显式赋值时从0递增 | 必须全显式赋值(否则报错) |
| 值类型支持 | 整数、小数(不支持Bigint)、计算式 | 字符串(不支持表达式,如['a'].join('')报错) |
| 反向映射 | 支持(通过值获取成员名) | 不支持 |
| 类型兼容性 | 可赋值为number(如let c:number = Color.Red) | 不可赋值为原生字符串(如let s:MyEnum = 'One'报错) |
| 示例 | enum Color { Red=7, Green, Blue=1<<3 } | enum Dir { Up='UP', Down='DOWN' } |
补充说明:
- 计算式支持:数值Enum成员可使用计算式(如位运算、函数返回值),示例:
enum Permission { UserRead = 1 << 8 } // 256 - 成员值唯一性:数值Enum允许成员值相同(如
Red=0, Green=0),但会失去区分意义,不推荐。
三、同名Enum合并
多个同名Enum会自动合并,需满足3条规则:
- 初始值规则:仅一个Enum的首成员可省略初始值(否则报错,避免递增歧义);
- 成员名规则:无同名成员(如两个Enum都有
B成员会报错); - 类型一致性规则:所有Enum需同为
const enum或同为非const enum(混合使用报错)。
示例(合法合并):
enum Foo { A } // 首成员省略初始值(A=0)
enum Foo { B=1 } // 合并后Foo={A:0, B:1}
四、keyof 运算符与反向映射
-
keyof 运算符
用于获取Enum的成员名联合类型,需配合typeof(否则keyof Enum等同于keyof number,返回数值原生方法名):enum MyEnum { A='a', B='b' } type MemberNames = keyof typeof MyEnum; // 'A'|'B' -
反向映射(仅数值Enum)
- 定义:通过成员值反向获取成员名(如
Weekdays[3]→ ‘Wednesday’); - 原理:数值Enum编译后会生成两组赋值(
Enum[成员名]=值+Enum[值]=成员名),示例:// 编译前(数值Enum) enum Weekdays { Monday=1, Tuesday=2 } // 编译后(关键代码) Weekdays["Monday"] = 1; Weekdays[1] = "Monday"; // 反向映射的核心 - 限制:字符串Enum编译后仅生成
Enum[成员名]=值,无反向映射。
- 定义:通过成员值反向获取成员名(如
五、使用注意事项
- 谨慎使用Enum:Enum并非TS“类型增强”核心功能,编译后会生成冗余对象(const enum可避免),优先用
as const或联合类型替代; - 避免同名冲突:Enum编译后为全局对象,不可与其他变量(函数、类、对象)同名;
- 数值Enum类型宽松:数值Enum作为类型时,允许赋值为任意
number(如foo(33)对foo(noYes:Bool)不报错),需自行确保取值合法性。
4. 关键问题
问题1:Enum 与 as const 断言的核心差异是什么?为何官方建议优先考虑 as const?
答案:
两者核心差异体现在“语言原生性”与“编译产物”:
- Enum:TS特有类型,编译后生成冗余JavaScript对象(非const Enum),虽兼具类型与值,但增加代码体积;支持反向映射(数值Enum)、同名合并等特有功能,语义更明确但灵活性较低。
- as const:基于JavaScript原生对象,通过断言将对象转为“只读+值类型”,无额外编译产物;仅具备“只读常量聚合”功能,不支持反向映射、合并,但更符合JS生态,无冗余代码。
官方建议优先用as const的原因是:Enum会引入非JS原生的冗余结构,而as const依托原生对象,既能实现“常量聚合+只读”,又避免编译后多余代码,更符合TS“增强JS而非替代JS”的定位。
问题2:为何反向映射仅存在于数值Enum,字符串Enum不支持?请从编译原理角度解释。
答案:
反向映射的核心是“通过成员值获取成员名”,其可行性取决于Enum编译后的赋值逻辑:
- 数值Enum:编译后会生成两组赋值语句:第一组为“成员名→值”(如
Weekdays["Monday"] = 1),第二组为“值→成员名”(如Weekdays[1] = "Monday"),两组赋值共同构成反向映射的基础。 - 字符串Enum:编译后仅生成一组赋值语句(“成员名→值”,如
Dir["Up"] = "UP"),无“值→成员名”的反向赋值,因此无法通过字符串值获取成员名,即不支持反向映射。
简言之,反向映射的本质是编译后的双向赋值,字符串Enum缺少反向赋值步骤,故无此功能。
问题3:字符串Enum相比数值Enum有哪些特殊限制?这些限制的原因是什么?
答案:
字符串Enum有3点特殊限制,均与“值的确定性”和“类型安全性”相关:
-
必须全显式赋值:
原因:数值Enum可通过“递增”默认生成值(如Red=0, Green=1),但字符串无天然递增规则,若省略赋值会导致值不确定,因此TS强制要求全显式赋值。 -
成员值不支持表达式:
原因:表达式(如['a','b'].join(''))的结果需运行时计算,可能导致值不可预测;而字符串Enum的核心场景是“固定字符串常量”,TS通过禁止表达式确保值的确定性。 -
不可赋值为原生字符串:
原因:数值Enum本质是number的子类型(可赋值为number),但字符串Enum是独立类型(非string子类型),TS通过此限制强化类型语义,避免“Enum类型”与“原生字符串”混用导致的类型模糊(如let s:MyEnum = 'One'报错,确保s仅能取Enum成员)。
