Welcome to wechat public account: Front Reading Room

introduce

Using enumerations we can define constants with names. Use enumerations to clearly express intent or to create a differentiated set of use cases. TypeScript supports numeric and string-based enumerations.

Digital enumeration

First let’s look at numeric enumerations, which you’ll be familiar with if you’ve used other programming languages.

enum Direction {
    Up = 1,
    Down,
    Left,
    Right
}
Copy the code

As above, we define a numeric enumeration with Up initialized to 1. The rest of the members will automatically grow from 1. In other words, direction. Up is 1, Down is 2, Left is 3, and Right is 4.

We can also dispense with initializers altogether:

enum Direction {
    Up,
    Down,
    Left,
    Right,
}
Copy the code

Now, the Up value is 0, the Down value is 1 and so on. This self-growing behavior is useful when we don’t care about the value of the member, but be aware that the value of each enumerator is different.

Using enumerations is simple: access enumeration members by enumeration properties and access enumeration types by enumeration names:

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

respond("Princess Caroline", Response.Yes)
Copy the code

Numeric enumerations can be mixed in with computed and constant members (as shown below). Briefly, enumerations without initializers are either placed first or after enumerations initialized with numeric constants or other constants. In other words, the following situations are not allowed:

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

This is allowed

enum E {
  B,
  A = getSomeValue()
}
Copy the code

String enumeration

The concept of string enumerations is simple, but has subtle runtime differences. In a string enumeration, each member must be initialized with either a string literal or another string enumeration member.

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

Because string enumerations have no self-growing behavior, string enumerations can be serialized well. In other words, if you are debugging and have to read a number of enumeration values at runtime, this value is usually difficult to read – it doesn’t useful information (though reverse mapping will help), string enumeration allows you to provide the meaning of a runtime and readable values, independent of the members of the enumeration name.

Heterogeneous enumeration (Heterogeneous enums)

Technically, enumerations can mix strings and numeric members, but it doesn’t seem like you’d do that:

enum BooleanLikeHeterogeneousEnum {
    No = 0,
    Yes = "YES",}Copy the code

Unless you really want to take advantage of JavaScript runtime behavior, we don’t recommend doing so.

Computed and constant members

Each enumerator takes a value, which can be constant or computed. Enumerators are treated as constants if:

1. It is the first member of the enumeration and has no initializer, in which case it is given the value 0:

// E.X is constant:
enum E { X }
Copy the code

2. It has no initializer and the enumerator before it is a numeric constant. In this case, the value of the current enumerator is incremented by the value of its previous enumerator.

// All enum members in 'E1' and 'E2' are constant.

enum E1 { X, Y, Z }

enum E2 {
    A = 1, B, C
}
Copy the code

3. Enumerators are initialized with constant enumeration expressions. Constant enumerated expressions are a subset of TypeScript expressions that can be evaluated at compile time. An expression is a constant enumerated expression when it satisfies one of the following conditions:

  • An enumerated expression literal (mainly a string literal or a number literal)
  • A reference to a previously defined constant enumerator (which can be defined in a different enumeration type)
  • A bracketed constant enumeration expression
  • One of the unary operators +, -, ~ applies to constant enumeration expressions
  • Constant enumeration expression as a binary operator, +, -, *, /, %, < <, > >, > > >, &, |, ^ operation object. If the constant enumeration expression evaluates to NaN or Infinity, an error is reported at compile time.

Enumerators in all other cases are treated as values that need to be computed.

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

The type of the associative enumeration with the enumerator

There is a special subset of noncomputed constant enumerators: literal enumerators. A literal enumerator is a constant enumerator that has no initial value, or whose value is initialized to

  • Any string literal (e.g. “foo”, “bar”, “baz”)
  • Any numeric literal (e.g., 1, 100)
  • Numeric literals with unary – symbols applied (e.g. -1, -100)

When all enumerators have literal enumeration values, it takes on a special semantics.

First, enumerators become types! For example, we can say that some members can only be values of enumerators:

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: 100,}Copy the code

Another change is that the enumeration type itself becomes a union of each enumeration member. Although we haven’t talked about [union Types](./Advanced Types. Md# union-types), you just need to know that with union enumerations, the type system can take advantage of the fact that it can know the set of values in an enumeration. As a result, TypeScript can catch stupid errors when comparing values. Such as:

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'.}}Copy the code

In this case, we first check to see if x is not e.foo. If passed the inspection, then | | effect of short circuit happens, the if statement in the body of the content will be executed. However, if this check fails, x will only be E.foo, so there is no reason to check if it is E.bar.

Enumeration at runtime

Enumerations are objects that actually exist at run time. For example, the following enumeration:

enum E {
    X, Y, Z
}
Copy the code

can actually be passed around to functions

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

// Works, since 'E' has a property named 'X' which is a number.
f(E);
Copy the code

Reverse mapping

In addition to creating an object with attribute names as object members, numeric enumerators also have reverse mapping from enumeration values to enumeration names. For example, in the following example:

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

An enumeration type is compiled into an object that contains both a forward mapping (name -> value) and a reverse mapping (value -> name). Reference enumerators are always born to access properties and are never inlined.

Note that no reverse mapping is generated for string enumerators.

Const enumeration

In most cases, enumerations are a very efficient solution. In some cases, however, the requirements are strict. To avoid the overhead in extra generated code and additional indirect access to enumeration members, we can use const enumerations. Constant enumerations are defined by using a const modifier on the enumeration.

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

Constant enumerations can only use constant enumeration expressions, and unlike regular enumerations, they are removed at compile time. Constant enumerators are inlined where they are used. This can be done because constant enumerations are not allowed to contain computed members.

const enum Directions {
    Up,
    Down,
    Left,
    Right
}

let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right]
Copy the code

External enumeration

An external enumeration is used to describe the shape of an existing enumeration type.

declare enum Enum {
    A = 1,
    B,
    C = 2
}
Copy the code

There is an important difference between external and non-external enumerations. In normal enumerations, members that have no initialization methods are treated as constant members. External enumerations of nonconstant numbers are treated as computations when there is no initialization method.

Welcome to wechat public account: Front Reading Room