枚举是一种组织相关值集合的方法。许多其他编程语言(C / C#/ Java)具有enum
数据类型,而JavaScript没有。但是,TypeScript可以。这是TypeScript枚举的示例定义:
enum CardSuit {
Clubs,
Diamonds,
Hearts,
Spades
}
// Sample usage
var card = CardSuit.Clubs;
// Safety
card = "not a member of card suit"; // Error : string is not assignable to type `CardSuit`
这些枚举值是number
类型 s,因此从此以后我将它们称为Number Enums。
TypeScript枚举是基于数字的。这意味着number可以被分配给enum的一个实例,其他任何与number兼容的东西也可以。
enum Color {
Red,
Green,
Blue
}
var col = Color.Red;
col = 0; // Effectively same as Color.Red
在进一步研究枚举之前,让我们看一下它生成的JavaScript,这是一个示例TypeScript:
enum Tristate {
False,
True,
Unknown
}
生成以下JavaScript:
var Tristate;
(function (Tristate) {
Tristate[Tristate["False"] = 0] = "False";
Tristate[Tristate["True"] = 1] = "True";
Tristate[Tristate["Unknown"] = 2] = "Unknown";
})(Tristate || (Tristate = {}));
让我们关注一下Tristate[Tristate["False"] = 0] = "False";
这一行。其中 Tristate["False"] = 0
应该是不言自明的,即设置Tristate
变量的"False"
成员为 0
。注意,在JavaScript中,赋值操作符返回赋值值(在本例中为 0
),因此JavaScript运行时执行的下一件事是 Tristate[0] = "False"
。这意味着您可以使用Tristate
变量将字符串版本的enum转换为数字,或将数字版本的enum转换为字符串。如下所示:
enum Tristate {
False,
True,
Unknown
}
console.log(Tristate[0]); // "False"
console.log(Tristate["False"]); // 0
console.log(Tristate[Tristate.False]); // "False" because `Tristate.False == 0`
默认情况下,枚举是基于0
的,然后每个后续值自动加1。作为一个例子,考虑以下情况:
enum Color {
Red, // 0
Green, // 1
Blue // 2
}
但是,可以通过专门分配给任何枚举成员来更改与该枚举成员关联的数字。如下所示,我们从3开始并从那里开始递增:
enum Color {
DarkRed = 3, // 3
DarkGreen, // 4
DarkBlue // 5
}
提示:我通常用来初始化第一个枚举的值
= 1
,因为它允许我对枚举值进行安全的真实检查。
枚举的一个出色用法是将枚举用作标志
的能力。标记允许您检查一组条件中的某个条件是否为真。考虑下面的例子,我们有一组关于动物的属性:
enum AnimalFlags {
None = 0,
HasClaws = 1 << 0,
CanFly = 1 << 1,
EatsFish = 1 << 2,
Endangered = 1 << 3
}
这里,我们使用左移运算符将1移动一定级别的位,从而得到按位不相交的数字0010
0100
1000
(如果你好奇的话,这些是小数1
2
4
8
)。bitwise操作符|
(or) / &
(and) / ~
(not)是你在使用flags时最好的朋友,如下所示:
enum AnimalFlags {
None = 0,
HasClaws = 1 << 0,
CanFly = 1 << 1,
}
type Animal = {
flags: AnimalFlags
}
function printAnimalAbilities(animal: Animal) {
var animalFlags = animal.flags;
if (animalFlags & AnimalFlags.HasClaws) {
console.log('animal has claws');
}
if (animalFlags & AnimalFlags.CanFly) {
console.log('animal can fly');
}
if (animalFlags == AnimalFlags.None) {
console.log('nothing');
}
}
let animal: Animal = { flags: AnimalFlags.None };
printAnimalAbilities(animal); // nothing
animal.flags |= AnimalFlags.HasClaws;
printAnimalAbilities(animal); // animal has claws
animal.flags &= ~AnimalFlags.HasClaws;
printAnimalAbilities(animal); // nothing
animal.flags |= AnimalFlags.HasClaws | AnimalFlags.CanFly;
printAnimalAbilities(animal); // animal has claws, animal can fly
这里:
|=
&=``~
|
组合标志注意:您可以结合使用标志在枚举定义内创建方便的快捷方式,例如:
EndangeredFlyingClawedFishEating
enum AnimalFlags {
None = 0,
HasClaws = 1 << 0,
CanFly = 1 << 1,
EatsFish = 1 << 2,
Endangered = 1 << 3,
EndangeredFlyingClawedFishEating = HasClaws | CanFly | EatsFish | Endangered,
}
我们只研究了成员值为数字的枚举。实际上,您也可以拥有带有字符串值的枚举成员。如:
export enum EvidenceTypeEnum {
UNKNOWN = '',
PASSPORT_VISA = 'passport_visa',
PASSPORT = 'passport',
SIGHTED_STUDENT_CARD = 'sighted_tertiary_edu_id',
SIGHTED_KEYPASS_CARD = 'sighted_keypass_card',
SIGHTED_PROOF_OF_AGE_CARD = 'sighted_proof_of_age_card',
}
因为它们提供了有意义的/可调试的字符串值,所以更容易处理和调试。
可以使用这些值进行简单的字符串比较。例如:
// Where `someStringFromBackend` will be '' | 'passport_visa' | 'passport' ... etc.
const value = someStringFromBackend as EvidenceTypeEnum;
// Sample use in code
if (value === EvidenceTypeEnum.PASSPORT){
console.log('You provided a passport');
console.log(value); // `passport`
}
如果您具有如下的枚举定义:
enum Tristate {
False,
True,
Unknown
}
var lie = Tristate.False;
var lie = Tristate.False
该行被编译为JavaScript var lie = Tristate.False
(是的,输出与输入相同)。这意味着,在执行运行时将需要查找Tristate
,然后Tristate.False
。为了提高性能,您可以将enum
标记为const enum
。如下所示:
const enum Tristate {
False,
True,
Unknown
}
var lie = Tristate.False;
生成JavaScript:
var lie = 0 ;
即编译器:
0
而不是Tristate.False
)。Tristate
运行时没有变量)。内联具有明显的性能优势。事实上,在运行时没有Tristate
变量,这只是编译器通过不生成运行时不实际使用的JavaScript来帮助您解决问题。然而,您可能希望编译器仍然生成枚举定义的JavaScript版本,如我们看到的数字到字符串或字符串到数字查找。在这种情况下,您可以使用编译器标志--preserveConstEnums
,它仍然会生成变量var Tristate
定义,这样您就可以在运行时手动使用Tristate["False"]
或 Tristate[0]
。这不会以任何方式影响内联。
您可以使用声明enum
+namespace
合并来向enum添加静态方法。下面演示了一个将静态成员isBusinessDay
添加到enum Weekday
的例子:
enum Weekday {
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday
}
namespace Weekday {
export function isBusinessDay(day: Weekday) {
switch (day) {
case Weekday.Saturday:
case Weekday.Sunday:
return false;
default:
return true;
}
}
}
const mon = Weekday.Monday;
const sun = Weekday.Sunday;
console.log(Weekday.isBusinessDay(mon)); // true
console.log(Weekday.isBusinessDay(sun)); // false
注意:仅当不使用模块时,开放式枚举才有意义。您应该使用模块。因此,本节最后。
这是为再次显示的枚举生成的JavaScript:
var Tristate;
(function (Tristate) {
Tristate[Tristate["False"] = 0] = "False";
Tristate[Tristate["True"] = 1] = "True";
Tristate[Tristate["Unknown"] = 2] = "Unknown";
})(Tristate || (Tristate = {}));
我们已经解释了Tristate[Tristate["False"] = 0] = "False";
部分。现在注意周围的代码(function (Tristate) { /*code here */ })(Tristate || (Tristate = {}));
指定(Tristate || (Tristate = {}));
部分。这基本上捕获了一个局部变量TriState
,该变量要么指向一个已经定义好的Tristate
值,要么用一个新的空{}
对象初始化它。
这意味着您可以跨多个文件分割(和扩展)一个枚举定义。例如下面我们将Color
的定义分成两个块
enum Color {
Red,
Green,
Blue
}
enum Color {
DarkRed = 3,
DarkGreen,
DarkBlue
}
请注意,您应该在枚举的延续中重新初始化第一个成员(这里DarkRed = 3
),以获取生成的代码而不是先前定义的clobber值(例如,0
1
,…)值)。如果你不这样做,TypeScript会警告您(In an enum with multiple declarations, only one declaration can omit an initializer for its first enum element.
)。