在第一节的基础数据类型中,有简单地提到过枚举 enum
,在这一节,将详细地介绍它。
数字枚举
先来举一个小例子:
enum Direction {
Up = 1,
Down,
Left,
Right,
}
如上,我们定义了一个数字枚举,
Up
使用初始化为1
。 其余的成员会从1
开始自动增长。 换句话说,Direction.Up
的值为1
,Down
为2
,Left
为3
,Right
为4
。
数字枚举可以被混入到计算过的和常量成员(如下所示)。 简短地说,不带初始化器的枚举或者被放在第一的位置,或者被放在使用了数字常量或其它常量初始化了的枚举后面。
function getSomeValue() {
return 1;
}
enum E {
A,
B = getSomeValue(),
}
它的其它基础内容,可以直接回去基础数据类型一节继续阅读。
字符串枚举
字符串枚举的概念很简单,但是有细微的运行时的差别。 在一个字符串枚举里,每个成员都必须用字符串字面量,或另外一个字符串枚举成员进行初始化。
enum Direction {
Up = "UP",
Down = "DOWN",
Left = "LEFT",
Right = "RIGHT",
}
由于字符串枚举没有自增长的行为,字符串枚举可以很好的序列化。 换句话说,如果你正在调试并且必须要读一个数字枚举的运行时的值,这个值通常是很难读的 - 它并不能表达有用的信息(尽管反向映射会有所帮助),字符串枚举允许你提供一个运行时有意义的并且可读的值,独立于枚举成员的名字。
异构枚举(不常用)
enum BooleanLikeHeterogeneousEnum {
No = 0,
Yes = "YES",
}
计算的/常量 成员
每个枚举成员都带有一个值,它可以是 常量 或 计算 出来的。
- 它是枚举的第一个成员且没有初始化器,这种情况下它被赋予值
0
// E.X is constant:
enum E { X }
- 它不带有初始化器且它之前的枚举成员是一个数字常量。 这种情况下,当前枚举成员的值为它上一个枚举成员的值加1。
enum E1 { X, Y, Z }
enum E2 {
A = 1, B, C,
}
- 枚举成员使用常量枚举表达式初始化。 常量枚举表达式是TypeScript表达式的子集,它可以在编译阶段求值。 当一个表达式满足下面条件之一时,它就是一个常量枚举表达式
- 一个枚举表达式字面量(主要是字符串字面量或数字字面量)
- 一个对之前定义的常量枚举成员的引用(可以是在不同的枚举类型中定义的)
- 带括号的常量枚举表达式
- 一元运算符
+
,-
,~
其中之一应用在了常量枚举表达式 - 常量枚举表达式做为二元运算符
+
,-
,*
,/
,%
,<<
,>>
,>>>
,&
,|
,^
的操作对象。若常量枚举表达式求值后为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
)
当所有枚举成员都拥有字面量枚举值时,它就带有了一种特殊的语义。
首先,枚举成员成为了类型! 例如,我们可以说某些成员只能是枚举成员的值:
enum ShapeKind {
Circle,
Square,
}
interface Circle {
kind: ShapeKind.Circle;
radius: number;
}
interface Square {
kind: ShapeKind.Square;
sideLength: number;
}
let c: Circle = {
radius: 100,
kind: ShapeKind.Circle,
};
运行时枚举
枚举是在运行时真正存在的对象。
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.
console.log(f(E));
反向映射
除了创建一个以属性名做为对象成员的对象之外,数字枚举成员还具有了反向映射,从枚举值到枚举名字。
enum Enum {
A,const enum Enum {
A = 1,
B = A * 2,
}
}
let a = Enum.A;
let nameOfA = Enum[a]; // "A"
const枚举
大多数情况下,枚举是十分有效的方案。 然而在某些情况下需求很严格。 为了避免在额外生成的代码上的开销和额外的非直接的对枚举成员的访问,我们可以使用const
枚举。 常量枚举通过在枚举上使用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 */];
外部枚举
外部枚举(Ambient Enums)是使用 declare enum
定义的枚举类型。
declare enum Directions {
Up,
Down,
Left,
Right,
}
let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];
//编译结果
var directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];
declare
定义的类型只会用于编译时的检查,编译结果中会被删除。
我们也可以同时使用 declare
和 const
declare 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 */];