跳到主要内容

TS 枚举

示例代码:typescript-demos

使用枚举我们可以定义一些带名字的常量。1

枚举的类型

数字枚举

定义枚举

enum Direction {
Up,
Down,
Left,
Right
}

默认从 0 开始自动增长,即:Up = 0, Down = 1, Left = 2...

如果想改变初始值,则使用初始化器(initializer):

enum Direction {
Up = 1, // 手动赋值,后面自动增长
Down, // =2
Left = 4,
Right // =5
}

使用枚举

// 使用枚举(支持反向映射)
console.log(Direction[1], Direction.Up); // Up 1(这里用 `Up` 或 `1` 都能找到对应的枚举值)


// 如果枚举成员包含非法字符,可以使用方括号来引用
enum HttpRequestField {
'Accept',
'Accept-Charset',
'Accept-Datetime',
'Accept-Encoding',
'Accept-Language',
}
console.log(HttpRequestField['Accept-Charset']); // 1

字符串枚举

在一个字符串枚举里,每个成员都必须用字符串字面量或另外一个字符串枚举成员进行初始化。

// 定义枚举
enum Direction {
Up = 'UP',
Down = 'DOWN',
Left = 'LEFT',
Right = 'RIGHT',
}

// 使用枚举
console.log(Direction['Right'], Direction.Right); // RIGHT RIGHT
// console.log(Direction['RIGHT']); // Error:不支持字符串枚举的反向映射

注意:TS 不支持基于字符串枚举的反向映射

异构枚举

英文:Heterogeneous enums

从技术上,枚举可以混合字符串和数字成员:

enum BooleanLikeEnum {
No = 0,
Yes = 'YES',
}

目前 TypeScript 只支持将数字和字符串作为枚举成员值。不允许使用其他值,比如 symbols

指定枚举成员值的方式

字面量枚举成员

enum NoYes {
No = 'No',
Yes = 'Yes'
}

function func(x: NoYes) {
return x;
}

console.log(func(NoYes.No)); // No
console.log(func(NoYes['No'])); // No
console.log(func(NoYes.Yes)); // Yes
console.log(func(NoYes['Yes'])); // Yes

常量枚举成员

如果可以在编译时计算枚举成员的值,则该枚举成员是常量。既可以隐式指定(TS 自增长指定,仅限数字枚举),也是显式指定且可以允许使用一下语法:

  • 数字字面量或字符串字面量
  • 对先前定义的常量枚举成员的引用
  • 括号
  • 一元运算符 +-~
  • 二进制运算符 +-*/%<<>>>>>&|^
enum Perm {
UserRead = 1 << 8,
UserWrite = 1 << 7,
UserExecute = 1 << 6,
GroupRead = 1 << 5,
GroupWrite = 1 << 4,
GroupExecute = 1 << 3,
AllRead = 1 << 2,
AllWrite = 1 << 1,
AllExecute = 1 << 0,
}
console.log(Perm.UserRead); // 256

计算枚举成员

可以通过任意表达式设置枚举成员的值:

enum NoYesSum {
No = 123,
Yes = Math.random(),
}
console.log(NoYesSum.Yes); // OK

枚举的原理

JS 对象一般都是正向映射(根据属性获取值),为什么这里枚举可以反向映射(根据值获取属性)?

依然拿 NoYes 枚举举例:

enum NoYes {
Yes,
No,
}

tsc 编译后,可以看到代码变成这样:

var NoYes;
(function (NoYes) {
NoYes[NoYes["Yes"] = 0] = "Yes";
NoYes[NoYes["No"] = 1] = "No";
})(NoYes || (NoYes = {}));
console.log(NoYes);

这里 NoYes["Yes"] = 0 返回了 0,也就是 NoYes[NoYes["Yes"] = 0] = "Yes"; 等于 NoYes[0] = "Yes";

也就是说,这里执行了两组赋值:

NoYes["No"] = 0;
NoYes["Yes"] = 1;

NoYes[0] = "No";
NoYes[1] = "Yes";

实际上,把 NoYes 枚举打印出来会看到这是一个这样的对象:

{
0: "Yes",
1: "No",
No: 1,
Yes: 0
}

但如果是字符串枚举:

enum NoYes {
Yes = 'YES',
No = 'NO',
}
console.log(NoYes)

编译后是这样:

var NoYes;
(function (NoYes) {
NoYes["Yes"] = "YES";
NoYes["No"] = "NO";
})(NoYes || (NoYes = {}));
console.log(NoYes);

打印出来的对象是这样:

{
No: "NO",
Yes: "YES"
}

所以 字符串枚举不支持反向映射

仅在数字枚举建立反向关系,是为了弥补数字枚举在运行时的可读性缺陷:

string enums allow you to give a meaningful and readable value when your code runs, independent of the name of the enum member itself. —— TypeScript Handbook 2

命名规范

TypeScript 手册使用 以大写字母开头的驼峰式名称,这是标准的 TypeScript 风格。3

例如, NoYes 枚举。

参考

Footnotes

  1. 枚举 · TypeScript中文网

  2. TypeScript: Handbook - Enums

  3. 一文让你彻底掌握 TS 枚举