Skip to main content

枚举类型

枚举(Enum)类型用于取值被限定在一定范围内的场景,比如一周只能有七天,颜色限定为红绿蓝等。

数字枚举

默认赋值

枚举成员会被赋值为从 0 开始递增的数字,同时也会对枚举值到枚举名进行反向映射:

enum Direction {
Up,
Down,
Left,
Right
}
console.log(Direction.Up === 0); // true
console.log(Direction.Down === 1); // true
console.log(Direction.Left === 2); // true
console.log(Direction.Right === 3); // true

console.log(Direction[0] === "UP"); // true
console.log(Direction[1] === "Down"); // true
console.log(Direction[2] === "Left"); // true
console.log(Direction[3] === "Right"); // true

事实上,上面的例子会被编译为:

"use strict";
var Direction;
(function (Direction) {
Direction[Direction["Up"] = 0] = "Up";
Direction[Direction["Down"] = 1] = "Down";
Direction[Direction["Left"] = 2] = "Left";
Direction[Direction["Right"] = 3] = "Right";
})(Direction || (Direction = {}));
console.log(Direction.Up === 0);
console.log(Direction.Down === 1);
console.log(Direction.Left === 2);
console.log(Direction.Right === 3);

console.log(Direction[0] === "UP");
console.log(Direction[1] === "Down");
console.log(Direction[2] === "Left");
console.log(Direction[3] === "Right");

Direction[Direction["Up"] = 0] = "Up",也就是 Direction[0] = "Up",所以我们可以把枚举类型看成一个JavaScript对象,而由于其特殊的构造,导致其拥有正反向同时映射的特性。

手动赋值

enum Direction {
Up = 6,
Down,
Left,
Right
}
console.log(Direction.Up === 6); // true
console.log(Direction.Down === 7); // true
console.log(Direction.Left === 8); // true
console.log(Direction.Right === 9); // true

未手动赋值的枚举项会接着上一个枚举项递增。

如果未手动赋值的枚举项与手动赋值的重复了,TypeScript 是不会察觉到这一点的

enum Direction {
Up = 3,
Down = 1,
Left,
Right
}
console.log(Direction.Up === 3); // true
console.log(Direction.Down === 1); // true
console.log(Direction.Left === 2); // true
console.log(Direction.Right === 3); // true

上面的例子中,递增到 3 的时候与前面的 Up 的取值重复了,但是 TypeScript 并没有报错,导致 Direction[3] 的值先是 Up,而后又被 Left 覆盖了。编译的结果是:

"use strict";
var Direction;
(function (Direction) {
Direction[Direction["Up"] = 3] = "Up";
Direction[Direction["Down"] = 1] = "Down";
Direction[Direction["Left"] = 2] = "Left";
Direction[Direction["Right"] = 3] = "Right";
})(Direction || (Direction = {}));
console.log(Direction.Up === 3);
console.log(Direction.Down === 1);
console.log(Direction.Left === 2);
console.log(Direction.Right === 3);

所以使用的时候需要注意,最好不要出现这种覆盖的情况。

字符串枚举

enum Direction {
Up = 'Up',
Down = 'Down',
Left = 'Left',
Right = 'Right'
}

console.log(Direction['Right'], Direction.Up); // Right Up

常量枚举

枚举其实可以被 const 声明为常量的,这样有什么好处?我们看以下例子:

const enum Direction {
Up = 'Up',
Down = 'Down',
Left = 'Left',
Right = 'Right'
}

const up = Direction.Up;

上例的编译结果是:

"use strict";
var up = "Up";

可以发现编译后,枚举类型消失了,这就是常量枚举的作用,常数枚举与普通枚举的区别是,它会在编译阶段被删除。

因为变量 up 已经使用过了枚举类型,之后就没有用了,也没有必要存在与 JavaScript 中了, TypeScript 在这一步就把 Direction 去掉了,我们直接使用 Direction 的值即可,这是性能提升的一个方案。

如果你想要保留常量枚举 ,那么可以添加编译选项 preserveConstEnums

联合枚举类型

enum Direction {
Up = 'Up',
Down = 'Down',
Left = 'Left',
Right = 'Right'
}

enum Animal {
Dog,
Cat
}

let direction: Direction = Direction.Up
let dog: Direction = Animal.Dog // ERROR: 不能将类型“Animal.Dog”分配给类型“Direction”。

up 声明为 Direction 类型,可以看成我们声明了一个联合类型 Direction.Up | Direction.Down | Direction.Left | Direction.Right,只有这四个类型其中的成员才符合要求。

枚举合并

enum Direction {
Up = 'Up',
Down = 'Down',
Left = 'Left',
Right = 'Right'
}

enum Direction {
Center = "Center"
}

let direction: Direction = Direction.Up

编译后:

"use strict";
var Direction;
(function (Direction) {
Direction["Up"] = "Up";
Direction["Down"] = "Down";
Direction["Left"] = "Left";
Direction["Right"] = "Right";
})(Direction || (Direction = {}));

(function (Direction) {
Direction["Center"] = "Center";
})(Direction || (Direction = {}));
var direction = Direction.Up;

枚举添加静态方法

使用 enum + namespace 的声明的方式向枚举类型添加静态方法。如下例所示,我们将静态成员 isBusinessDay 添加到枚举上:

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));

参考文档

深入理解 TypeScript

深入浅出TypeScript