TypeScript 中的枚举

2,136 阅读4分钟

这是我参与11月更文挑战的第15天,活动详情查看:2021最后一次更文挑战

枚举

接下来,我们学习 TS 中一个新概念:枚举,首先,我们先来看看这个简单的例子:

let myColor = 'Red';
let yourColor = 'Blue';

function isItRed(color) {
    return color === 'Red';
}

isItRed(myColor); // true
isItRed(yourColor); // false

上面这样实现有个缺点,外面的 myColor的 'Red' 和里面的 'Red',无法同步的更新。

这里我们有更好的实现方式,就是使用对象来存储这些字符串。这样的话,我们就可以将对应的字符串替换成对象中的某一项了。

替换完之后的好处,就是我们只需要更改 Colors 对象,就可以同步修改后面的内容。

其实我们根本不关心 Colors.Red 和 Colors.Blue 是什么。只需要它有一个值就行。

const Colors = {
    Red: 'Red',
    Blue: 'Blue'
}
let myColor = Colors.Red;
let yourColor = Colors.Blue;

function isItRed(color) {
    return color === Colors.Red;
}

isItRed(myColor); // true
isItRed(yourColor); // false

其实上面这个就是 TS 中枚举的原型。

enum Colors = {
    Red,
    Blue,
    Green
}
let myColor = Colors.Red;
let yourColor = Colors.Blue;

// 在使用这个方法的时候,我们就需要给这个 color 添加一个它的类型。
function isItRed(color: Colors) {
    return color === Colors.Red;
}

isItRed(myColor); // true
isItRed(yourColor); // false

// 它只能接受枚举类型的输入,我们输入一个错误的字符串类型的 'Red',就会报错
// 只能够输入枚举类型里面的某一项值
isItRed('Red');

默认为 0,1,2,我们也可以指定它为一个具体的值,这样的话,后面的项,会根据前面的项递增

enum Colors = {
    Red = 1,
    Blue,
    Green
}

enum Colors = {
    Red, // 这一项为 0
    Blue = 3, // 往后开始递增
    Green // 这一项为 4
}

// 除了赋值数字之外,我们还可以给它赋值字符串或者布尔,这样的话,这个枚举类型的每一项,就是字符串类型
enum Colors = {
    Red = 'Red',
    Blue = 'Blue',
    Green = 'Green'
}

习题-关于下面表达式,运行结果为 false 的是?

enum animal {
    name,
    weight
}

A. animal.name === 0;

B. typeof animal.name === 'string';

C. typeof animal.weight=== 'number';

D.animal['weight'] === 1

答案:B

解析

根据枚举的特性,枚举成员会被赋值为从 0 开始递增的数字。故错误选项为 B。

资料-常数项和计算所得项

枚举项有两种类型:常数项(constant member)和计算所得项(computed member)。

我们开看一个典型的计算所得项的例子:

enum Color {Red, Green, Blue = "blue".length};

上面的例子中,"blue".length 就是一个计算所得项。

上面的例子不会报错,但是如果紧接在计算所得项后面的是未手动赋值的项,那么它就会因为无法获得初始值而报错:

enum Color {Red = "red".length, Green, Blue};

// index.ts(1,33): error TS1061: Enum member must have initializer.
// index.ts(1,40): error TS1061: Enum member must have initializer.

下面是常数项和计算所得项的完整定义,部分引用自中文手册 - 枚举

当满足以下条件时,枚举成员被当作是常数:

不具有初始化函数并且之前的枚举成员是常数。在这种情况下,当前枚举成员的值为上一个枚举成员的值加 1。但第一个枚举元素是个例外。如果它没有初始化方法,那么它的初始值为 0。

枚举成员使用常数枚举表达式初始化。常数枚举表达式是 TypeScript 表达式的子集,它可以在编译阶段求值。当一个表达式满足下面条件之一时,它就是一个常数枚举表达式:

数字字面量

引用之前定义的常数枚举成员(可以是在不同的枚举类型中定义的)如果这个成员是在同一个枚举类型中定义的,可以使用非限定名来引用

带括号的常数枚举表达式

+, -, ~ 一元运算符应用于常数枚举表达式

+, -, *, /, %, <<, >>, >>>, &, |, ^ 二元运算符,常数枚举表达式做为其一个操作对象。若常数枚举表达式求值后为NaN或Infinity,则会在编译阶段报错

所有其它情况的枚举成员被当作是需要计算得出的值。

资料-常数枚举

常数枚举是使用 const enum 定义的枚举类型:

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 */];

假如包含了计算成员,则会在编译阶段报错:

const enum Color {Red, Green, Blue = "blue".length};

// index.ts(1,38): error TS2474: In 'const' enum declarations member initializer must be constant expression.