TypeScript 的 Enum 类型
Enum 是 TypeScript 新增的一种数据结构和类型,中文译为“枚举”。
简介
实际开发中,经常需要定义一组相关的常量。
const RED = 1;
const GREEN = 2;
const BLUE = 3;
let color = userInput();
if (color === RED) {/* */}
if (color === GREEN) {/* */}
if (color === BLUE) {/* */}
throw new Error('wrong color');
上面示例中,常量RED
、GREEN
、BLUE
是相关的,意为变量color
的三个可能的取值。它们具体等于什么值其实并不重要,只要不相等就可以了。
TypeScript 就设计了 Enum 结构,用来将相关常量放在一个容器里面,方便使用。
enum Color {
Red, // 0
Green, // 1
Blue // 2
}
上面示例声明了一个 Enum 结构Color
,里面包含三个成员Red
、Green
和Blue
。第一个成员的值默认为整数0
,第二个为1
,第三个为2
,以此类推。
使用时,调用 Enum 的某个成员,与调用对象属性的写法一样,可 以使用点运算符,也可以使用方括号运算符。
let c = Color.Green; // 1
// 等同于
let c = Color['Green']; // 1
Enum 结构本身也是一种类型。比如,上例的变量c
等于1
,它的类型可以是 Color,也可以是number
。
let c:Color = Color.Green; // 正确
let c:number = Color.Green; // 正确
上面示例中,变量c
的类型写成Color
或number
都可以。但是,Color
类型的语义更好。
Enum 结构的特别之处在于,它既是一种类型,也是一个值。绝大多数 TypeScript 语法都是类型语法,编译后会全部去除,但是 Enum 结构是一个值,编译后会变成 JavaScript 对象,留在代码中。
// 编译前
enum Color {
Red, // 0
Green, // 1
Blue // 2
}
// 编译后
let Color = {
Red: 0,
Green: 1,
Blue: 2
};
上面示例是 Enum 结构编译前后的对比。
由于 TypeScript 的定位是 JavaScript 语言的类型增强,所以官方建议谨慎使用 Enum 结构,因为它不仅仅是类型,还会为编译后的代码加入一个对象。
Enum 结构比较适合的场景是,成员的值不重要,名字更重要,从而增加代码的可读性和可维护性。
enum Operator {
ADD,
DIV,
MUL,
SUB
}
function compute(
op:Operator,
a:number,
b:number
) {
switch (op) {
case Operator.ADD:
return a + b;
case Operator.DIV:
return a / b;
case Operator.MUL:
return a * b;
case Operator.SUB:
return a - b;
default:
throw new Error('wrong operator');
}
}
compute(Operator.ADD, 1, 3) // 4
上面示例中,Enum 结构Operator
的四个成员表示四则运算“加减乘除”。代码根本不需要用到这四个成员的值,只用成员名就够了。
TypeScript 5.0 之前,Enum 有一个 Bug,就是 Enum 类型的变量可以赋值为任何数值。
enum Bool {
No,
Yes
}
function foo(noYes:Bool) {
// ...
}
foo(33); // TypeScript 5.0 之前不报错
上面示例中,函数foo
的参数noYes
是 Enum 类型,只有两个可用的值。但是,TypeScript 5.0 之前,任何数值作为函数foo
的参数,编译都不会报错,TypeScript 5.0 纠正了这个问题。
另外,由于 Enum 结构编译后是一个对象,所以不能有与它同名的变量(包括对象、函数、类等)。
enum Color {
Red,
Green,
Blue
}
const Color = 'red'; // 报错
上面示例,Enum 结构与变量同名,导致报错。
很大程度上,Enum 结构可以被对象的as const
断言替代。
enum Foo {
A,
B,
C,
}
const Bar = {
A: 0,
B: 1,
C: 2,
} as const;
if (x === Foo.A) {}
// 等同于
if (x === Bar.A) {}
上面示例中,对象Bar
使用了as const
断言,作用就是使得它的属性无法修改。这样的话,Foo
和Bar
的行为就很类似了,前者完全可以用后者替代,而且后者还是 JavaScript 的原生数据结构。
Enum 成员的值
Enum 成员默认不必赋值,系统会从零开始逐一递增,按照顺序为每个成员赋值,比如0、1、2……