原文地址 www.tslang.cn
枚举
数字枚举
enum Direction {
Up = 1,
Down,
Left,
Right
}
数字枚举可以被混入到 计算过的和常量成员(如下所示)。 简短地说,不带初始化器的枚举或者被放在第一的位置,或者被放在使用了数字常量或其它常量初始化了的枚举后面。 换句话说,下面的情况是不被允许的:
注释:不带初始化器的枚举被放在第一的位置会自动初始化为0。下例中的错误是因为 B 没有初始化值,并不是因为计算值放在了他前面
注释:计算值必须返回数字类型,并且只有数值枚举可以混入计算值枚举项。
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",
}
由于字符串枚举没有自增长的行为,字符串枚举可以很好的序列化。 换句话说,如果你正在调试并且必须要读一个数字枚举的运行时的值,这个值通常是很难读的 - 它并不能表达有用的信息(尽管 反向映射会有所帮助),字符串枚举允许你提供一个运行时有意义的并且可读的值,独立于枚举成员的名字。
异构枚举(Heterogeneous enums)
从技术的角度来说,枚举可以混合字符串和数字成员,但是似乎你并不会这么做:
enum BooleanLikeHeterogeneousEnum {
No = 0,
Yes = "YES",
}
除非你真的想要利用 JavaScript 运行时的行为,否则我们不建议这样做。
注释:异构枚举中被字符串枚举项隔断的后续数值枚举不会自动初始化。
计算的和常量成员
每个枚举成员都带有一个值,它可以是 _常量_或 计算出来的。 当满足如下条件时,枚举成员被当作是常量:
-
它是枚举的第一个成员且没有初始化器,这种情况下它被赋予值
0
:// E.X is constant: 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 表达式的子集,它可以在编译阶段求值。 当一个表达式满足下面条件之一时,它就是一个常量枚举表达式:
- 一个枚举表达式字面量(主要是字符串字面量或数字字面量)
- 一个对之前定义的常量枚举成员的引用(可以是在不同的枚举类型中定义的)
- 带括号的常量枚举表达式
- 一元运算符
+
,-
,~
其中之一应用在了常量枚举表达式 - 常量枚举表达式做为二元运算符
+
,-
,*
,/
,%
,<<
,>>
,>>>
,&
,|
,^
的操作对象。 若常数枚举表达式求值后为NaN
或Infinity
,则会在编译阶段报错。
所有其它情况的枚举成员被当作是需要计算得出的值。
注释:对自身枚举项的引用直接使用项目名
enum FileAccess {
// constant members
None,
Read = 1 << 1,
Write = 1 << 2,
ReadWrite = Read | Write,
// computed member
G = "123".length
}
联合枚举与枚举成员的类型
注释:在 4.1.2 中常量成员都具有以下特点,不一定要字面量枚举成员
注释:在 4.1.2 中数值字面量枚举类形成的类型实际是数值型和该枚举的联合
字面量枚举成员是指不带有初始值的常量枚举成员,或者是值被初始化为
- 任何字符串字面量(例如:
"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 = {
kind: ShapeKind.Square,
// ~~~~~~~~~~~~~~~~ Error!
radius: 100,
}
// 如下是正确的
let c: Circle = {
kind: 0,
radius: 100,
}
另一个变化是枚举类型本身变成了每个枚举成员的 联合。
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);
反向映射
除了创建一个以属性名做为对象成员的对象之外,数字枚举成员还具有了 反向映射,从枚举值到枚举名字。
枚举类型被编译成一个对象,它包含了正向映射( name
-> value
)和反向映射( value
-> name
)。
注释:虽然编译成的对象具有正反映射,但作这个对象进行类型检查时只包含正映射的键值对,例如:
enum E { A } function f(x: {A:number}) {} f(E) function f1(x: {0:string}){} f1(E) // Error
引用枚举成员总会生成为对属性访问并且永远也不会内联代码。
注释:当引用一个枚举的成员项时,实际是创建了一个枚举对象,然后引用这个对象的属性
要注意的是 _不会_为字符串枚举成员生成反向映射。
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 */];
外部枚举
外部枚举用来描述已经存在的枚举类型的形状。
declare enum Enum {
A = 1,
B,
C = 2
}
外部枚举和非外部枚举之间有一个重要的区别,在正常的枚举里,没有初始化方法的成员被当成常数成员。 对于非常数的外部枚举而言,没有初始化方法时被当做需要经过计算的。
注释:外部枚举仅仅用于编译时的检查,在编译结果中会被删除,是为了便于给第三方库定义枚举对象的类型
——————————————————————————————————————————————
注释:以下内容来至于 TS 3.1 - 类型兼容性
枚举
枚举类型与数字类型兼容,并且数字类型与枚举类型兼容。不同枚举类型之间是不兼容的。比如,
注释:枚举类型与字符串类型也是兼容的
enum Status { Ready, Waiting };
enum Color { Red, Blue, Green };
let status = Status.Ready;
status = Color.Green; // Error