Enums

枚举

枚举

枚举允许我们定义一组命名常量。使用枚举可以更容易地记录意图,或者创建一组不同的案例。TypeScript提供了数字和基于字符串的枚举。

数字枚举

我们首先从数字枚举开始,如果你来自其他语言,这可能更为熟悉。可以使用enum关键字定义枚举。

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

上面,我们有一个数字枚举,其中Up初始化为1。以下所有成员都从这一点开始自动递增。换句话说,Direction.Up价值1Down拥有2Left拥有3Right拥有4

如果我们想要,我们可以完全忽略初始化器:

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

这里Up有价值0Down会有1,等等。这种自动递增行为对于我们可能不关心成员值本身的情况是有用的,但是确实在意每个值与同一个枚举中的其他值不同。

使用枚举很简单:只需从枚举本身访问任何成员作为属性,并使用枚举的名称声明类型:

enum Response { No = 0, Yes = 1, } function respond(recipient: string, message: Response): void { // ... } respond("Princess Caroline", Response.Yes)

数字枚举可以在计算和常量成员中混合(见下文)。简而言之,没有初始化器的枚举要么必须是第一个,要么必须在用数字常量或其他常量枚举成员初始化的数字枚举之后。换句话说,以下是不允许的:

enum E { A = getSomeValue(), B, // error! 'A' is not constant-initialized, so 'B' needs an initializer }

字符串枚举

字符串枚举是一个类似的概念,但具有一些细微的运行时间差异,如下文所述。在一个字符串枚举中,每个成员必须用字符串文字或其他字符串枚举成员进行常量初始化。

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

虽然字符串枚举不具有自动递增行为,但字符串枚举具有“序列化”好处。换句话说,如果您正在调试并且必须读取数值枚举的运行时值,那么该值通常是不透明的 - 它本身没有传达任何有用的含义(尽管反向映射通常可以帮助),字符串枚举允许您在代码运行时提供有意义且可读的值,而与枚举成员本身的名称无关。

异构枚举

技术上的枚举可以与字符串和数字成员混合使用,但不清楚为什么你会这样做:

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

除非你真的想以聪明的方式利用JavaScript的运行时行为,否则建议你不要这样做。

计算和不断的成员

每个枚举构件都具有与其相关联的值,其可以是恒定的计算的。在以下情况下,enum成员被认为是常量:

  • 它是枚举中的第一个成员,它没有初始化器,在这种情况下,它被分配了值0:// EX是常量:枚举E {X}

- a literal enum expression (basically a string literal or a numeric literal) - a reference to previously defined constant enum member (which can originate from a different enum). - a parenthesized constant enum expression - one of the `+`, `-`, `~` unary operators applied to constant enum expression - `+`, `-`, `*`, `/`, `%`, `<<`, `>>`, `>>>`, `&`, `|`, `^` binary operators with constant enum expressions as operands It is a compile time error for constant enum expressions to be evaluated to `NaN` or `Infinity`.

在所有其他情况下,枚举成员被认为是计算的。

enum FileAccess { // constant members None, Read = 1 << 1, Write = 1 << 2, ReadWrite = Read | Write, // computed member G = "123".length }

联合枚举和枚举成员类型

有一个不计算常量枚举成员的特殊子集:字面枚举成员。文字枚举成员是一个常量枚举成员没有初始化值,或与初始化值

  • 任何字符串文字(例如"foo""bar"baz"

当枚举中的所有成员都有字面枚举值时,就会发挥一些特殊的语义。

首先,枚举成员也是类型的!例如,我们可以说某些成员只能拥有一个枚举成员的价值:

enum ShapeKind { Circle, Square, } interface Circle { kind: ShapeKind.Circle; radius: number; } interface Square { kind: ShapeKind.Square; sideLength: number; } let c: Circle = { kind: ShapeKind.Square, // ~~~~~~~~~~~~~~~~ Error! radius: number, }

另一个变化是枚举类型本身有效地成为每个枚举成员的联合。尽管我们尚未讨论union类型,但您需要知道的一点是,使用union枚举类型系统能够利用这样一个事实,即它知道enum本身存在的确切值集合。正因为如此,TypeScript可以捕捉到我们可能错误比较值的愚蠢错误。例如:

enum E { Foo, Bar, } function f(x: E) { if (x !== E.Foo || x !== E.Bar) { // ~~~~~~~~~~~ // Error! Operator '!==' cannot be applied to types 'E.Foo' and 'E.Bar'. } }

在该示例中,我们首先检查是否x没有 E.Foo。如果检查成功,那么我们||将会短路,'if'的主体将会运行。但是,如果检查没有成功,则x可以E.Foo,所以它是没有意义的,看它是否等于E.Bar

运行时枚举

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

enum E { X, Y, Z }

实际上可以传递给函数

function f(obj: { X: number }) { return obj.X; } // Works, since 'E' has a property named 'X' which is a number. f(E

反向映射

除了为成员创建具有属性名称的对象之外,数字枚举成员还可以获得从枚举值到枚举名称的反向映射。例如,在这个例子中:

enum Enum { A } let a = Enum.A; let nameOfA = Enum[a]; // "A"

TypeScript可能会将其编译为如下所示的JavaScript:

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

在这个生成的代码中,一个枚举被编译成一个存储forward(name- > value)和reverse(value- > name)映射的对象。对其他枚举成员的引用总是作为属性访问发出并且从不内联。

请记住,字符串枚举成员根本不会生成反向映射。

const 枚举

在大多数情况下,枚举是完全有效的解决方案。不过有时候需求会更紧张。为了避免在访问枚举值时支付额外的生成代码和额外间接成本,可以使用const枚举。常量枚举const在我们的枚举上使用修饰符来定义:

const enum Enum { A = 1, B = A * 2 }

常量枚举只能使用常量枚举表达式,而不像常规枚举,它们在编译期间会被完全删除。常量枚举成员在使用地点内联。这是可能的,因为常量枚举不能计算成员。

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 }

环境和非环境枚举之间的一个重要区别是,在常规枚举中,如果前一个枚举成员被认为是常量,那么没有初始化方的成员将被视为常量。相反,没有初始化器的环境(和非const)枚举成员始终被认为是计算的。