TS文档学习 --- 枚举

231 阅读4分钟

本篇翻译整理自 TypeScript Handbook 中 「Enums」 章节。

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

TypeScript支持数字的和基于字符串的枚举。

数字枚举

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

如上,我们定义了一个数字枚举,Up使用初始化为1。 其余的成员会从1开始自动增长。 换句话说,Direction.Up的值为1Down2Left3Right4

我们还可以完全不使用初始化器:

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

现在,Up的值为0Down的值为1等等。 当我们不在乎成员的值的时候,这种自增长的行为是很有用处的,但是要注意每个枚举成员的值都是不同的。

数字枚举可以被混入到计算过的和常量成员(如下所示)。 简短地说,没有初始化的成员要么在首位,要么必须在用数值常量或其他常量枚举成员初始化的数值枚举之后。 换句话说,下面的情况是不被允许的:

enum E {
    A = getSomeValue(),
    B, // Error! Enum member must have initializer.
}

但是以下写法是被允许的

enum E {
    A,
    B = E.A,
    C
}

字符串枚举

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

enum Direction {
    Up = "UP",
    Down = "DOWN",
    Left = "LEFT",
    Right = "RIGHT",
}

由于字符串枚举没有自增长的行为,字符串枚举可以很好的序列化

字符串枚举允许你提供一个运行时有意义的并且可读的值,独立于枚举成员的名字。

如果你正在调试并且必须要读一个数字枚举的运行时的值的时候,这往往是十分有用的

异构枚举

从技术的角度来说,枚举可以混合字符串和数字成员, 但是并不推荐

enum BooleanLikeHeterogeneousEnum {
    No = 0,
    Yes = "YES",
}

枚举成员类型

当一个枚举不带有初始值或者枚举的值全部为数值类型或者字符串类型的时候

这个枚举的每一个枚举成员就变成了一个类型,且枚举本身成为了每个枚举成员的联合类型

enum ShapeKind {
    Circle,
    Square,
}
// type ShapeKind = ShapeKind.Circle | ShapeKind.Square

interface Circle {
    kind: ShapeKind.Circle;
    radius: number;
}

interface Square {
    kind: ShapeKind.Square;
    sideLength: number;
}

运行时的枚举

枚举会改变程序执行的运行时,是在运行时真正存在的对象

枚举是在运行时真正存在的对象。 例如下面的枚举:

enum E {
    X, Y, Z
}

可以传递给函数

function f(obj: { X: number }) {
    return obj.X;
}

// 没问题,因为 'E'包含一个数值型属性'X'。
f(E);

编译时的枚举

enum LogLevel {
    ERROR, WARN, INFO, DEBUG
}

/**
 * 我们可以通过 keyof typeof 枚举来获取 一个枚举里所有字符串key的联合
 * 等同于:
 * type LogLevelStrings = 'ERROR' | 'WARN' | 'INFO' | 'DEBUG';
 */
type LogLevelStrings = keyof typeof LogLevel;

反向映射

除了创建一个以属性名做为对象成员的对象之外,数字枚举成员还具有了_反向映射_,从枚举值到枚举名字

enum Enum {
    A
}

会被编译为

var Enum;
(function (Enum) {
    Enum[Enum["A"] = 0] = "A";
})(Enum || (Enum = {}));
var a = Enum.A;
var nameOfA = Enum[a]; // "A"

生成的代码中,枚举类型被编译成一个对象,它包含了正向映射(name -> value)和反向映射(value -> name)。

要注意的是_不会_为字符串枚举成员生成反向映射

const枚举

大多数情况下,枚举是十分有效的方案。 然而在某些情况下需求很严格。 为了避免在额外生成的代码上的开销和额外的非直接的对枚举成员的访问,我们可以使用const枚举。 常量枚举通过在枚举上使用const修饰符来定义。

常量枚举只能使用常量枚举表达式,并且不同于常规的枚举,它们在编译阶段会被删除

所以常量枚举不允许包含计算成员。

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
}