非常教程

Typescript参考手册

枚举 | Enums

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}
  • 它没有一个初始化器,前面的枚举成员是一个数字常量。在这种情况下,当前枚举成员的值将是前一个枚举成员的值加1。//'E1'和'E2'中的所有枚举成员都是常量。枚举E1 {X,Y,Z}枚举E2 {A = 1,B,C}
  • 枚举成员用一个常量枚举表达式进行初始化。常量枚举表达式是TypeScript表达式的一个子集,可以在编译时对其进行全面评估。表达式是一个常量枚举表达式,如果它是:
- 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"
  • 任何数字文字(例如1100
  • 一元减号应用于任何数字文字(例如-1-100

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

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

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)枚举成员始终被认为是计算的。

枚举 | Enums相关

Typescript

TypeScript 是 JavaScript 的类型的超集,它可以编译成纯 JavaScript。编译出来的 JavaScript 可以运行在任何浏览器上。

主页 https://www.typescriptlang.org
源码 https://github.com/Microsoft/TypeScript
发布版本 2.6.0