目录
使用枚举我们可以定义一些带名字的常量。
使用枚举可以清晰地表达意图或创建一组有区别的用例。
TypeScript支持数字的和基于字符串的枚举。
默认起始值为0, 也可手动指定,如下例指定起始值为1
- enum Direction {
- Up = 1,
- Down,
- Left,
- Right
- }
使用枚举:通过枚举的属性来访问枚举成员,和枚举的名字来访问枚举类型:
- enum Response {
- No = 0,
- Yes = 1,
- }
-
- function respond(recipient: string, message: Response): void {
- // ...
- }
-
- respond("Princess Caroline", Response.Yes)
简短地说,不带初始化器的枚举或者被放在第一的位置,或者被放在使用了数字常量或其它常量初始化了的枚举后面。
换句话说,下面的情况是不被允许的:
- enum E {
- A = getSomeValue(),
- B, // error! 'A' is not constant-initialized, so 'B' needs an initializer
- }
在一个字符串枚举里,每个成员都必须用字符串字面量,或另外一个字符串枚举成员进行初始化。
- enum Direction {
- Up = "UP",
- Down = "DOWN",
- Left = "LEFT",
- Right = "RIGHT",
- }
由于字符串枚举没有自增长的行为,字符串枚举可以很好的序列化。
从技术的角度来说,枚举可以混合字符串和数字成员。
除非你真的想要利用JavaScript运行时的行为,否则不建议这么做。
- enum BooleanLikeHeterogeneousEnum {
- No = 0,
- Yes = "YES",
- }
每个枚举成员都带有一个值,它可以是 常量或 计算出来的。
当满足如下条件时,枚举成员被当作是常量:
- 它是枚举的第一个成员且没有初始化器,这种情况下它被赋予值 0:
- enum E { X }
-
- //----------------------------------------------------
- 它不带有初始化器且它之前的枚举成员是一个 数字常量。 这种情况下,
- 当前枚举成员的值为它上一个枚举成员的值加1。
- // All enum members in 'E1' and 'E2' are constant.
-
- enum E1 { X, Y, Z }
-
- enum E2 {
- A = 1, B, C
- }
枚举成员使用 常量枚举表达式初始化。 常数枚举表达式是TypeScript表达式的子集,它可以在编译阶段求值。 当一个表达式满足下面条件之一时,它就是一个常量枚举表达式:
1.一个枚举表达式字面量(主要是字符串字面量或数字字面量)+
, -
, ~
其中之一应用在了常量枚举表达式+
, -
, *
, /
, %
, <<
, >>
, >>>
, &
, |
, ^
的操作对象。 若常数枚举表达式求值后为 NaN
或 Infinity
,则会在编译阶段报错。- 所有其它情况的枚举成员被当作是需要计算得出的值。
- enum FileAccess {
- // constant members
- None,
- Read = 1 << 1,
- Write = 1 << 2,
- ReadWrite = Read | Write,
- // computed member
- G = "123".length
- }
存在一种特殊的非计算的常量枚举成员的子集:字面量枚举成员。
字面量枚举成员是指不带有初始值的常量枚举成员,或者是值被初始化为
"foo"
, "bar"
, "baz"
)1
, 100
)-
符号的数字字面量(例如: -1
, -100
)当所有枚举成员都拥有字面量枚举值时,它就带有了一种特殊的语义。
- 1,枚举成员成为了类型! 例如,我们可以说某些成员 只能是枚举成员的值:
- enum ShapeKind {
- Circle,
- Square,
- }
-
- interface Circle {
- kind: ShapeKind.Circle;
- radius: number;
- }
-
- interface Square {
- kind: ShapeKind.Square;
- sideLength: number;
- }
-
- let c: Circle = {
- kind: ShapeKind.Square,
- // ~~~~~~~~~~~~~~~~ Error!
- radius: 100,
- }
-
- //----------------------------------------------------------------
-
- 2.是枚举类型本身变成了每个枚举成员的 联合。
-
- enum E {
- Foo,
- Bar,
- }
-
- function f(x: E) {
- if (x !== E.Foo || x !== E.Bar) {
- // ~~~~~~~~~~~
- // Error! Operator '!==' cannot be applied to types 'E.Foo' and 'E.Bar'.
- }
- }
这个例子里,我们先检查 x
是否不是 E.Foo
。 如果通过了这个检查,然后 ||
会发生短路效果, if
语句体里的内容会被执行。 然而,这个检查没有通过,那么 x
则 只能为 E.Foo
,因此没理由再去检查它是否为 E.Bar
。
枚举是在运行时真正存在的对象。 例如下面的枚举:
- enum E {
- X, Y, Z
- }
-
- function f(obj: { X: number }) {
- return obj.X;
- }
-
- // Works, since 'E' has a property named 'X' which is a number.
- f(E);
除了创建一个以属性名做为对象成员的对象之外,数字枚举成员还具有了 反向映射,从枚举值到枚举名字。
- enum Enum {
- A
- }
- let a = Enum.A;
- let nameOfA = Enum[a]; // "A"
-
- //TypeScript可能会将这段代码编译为下面的JavaScript:
- var Enum;
- (function (Enum) {
- Enum[Enum["A"] = 0] = "A";
- })(Enum || (Enum = {}));
- var a = Enum.A;
- var nameOfA = Enum[a]; // "A"
生成的代码中,枚举类型被编译成一个对象,它包含了正向映射( name
-> value
)和反向映射( value
-> name
)。 引用枚举成员总会生成为对属性访问并且永远也不会内联代码。
⚠️注意: 不会为字符串枚举成员生成反向映射。
为了避免在额外生成的代码上的开销和额外的非直接的对枚举成员的访问,我们可以使用 const
枚举。
- const enum Enum {
- A = 1,
- B = A * 2
- }
常量枚举只能使用常量枚举表达式,并且不同于常规的枚举,它们在编译阶段会被删除。 常量枚举成员在使用的地方会被内联进来。 之所以可以这么做是因为,常量枚举不允许包含计算成员。
- const enum Directions {
- Up,
- Down,
- Left,
- Right
- }
-
- let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right]
-
- 生成后的代码为:
- var directions = [0 /* Up */, 1 /* Down */, 2 /* Left */, 3 /* Right */];
外部枚举(declare)用来描述已经存在的枚举类型的形状。
- declare enum Enum {
- A = 1,
- B,
- C = 2
- }
外部枚举和非外部枚举之间有一个重要的区别:
正常的枚举: 没有初始化方法的成员被当成常数成员。
非常数的外部枚举: 没有初始化方法时被当做需要经过计算的。