TypeScript enums (hyper-verbose)

The Enum type is used in scenarios where the values are limited to a certain range, for example, there can only be seven days in a week, and the colors are limited to red, green, and blue, etc. TS enumeration is inspired by C#

Simple example

Enumerations are defined using the enum keyword:

enum Days {<!-- -->Sun, Mon, Tue, Wed, Thu, Fri, Sat};

Enumeration members will be assigned values that increase from 0, and the reverse mapping of enumeration values to enumeration names will also be performed:

enum Days {<!-- -->Sun, Mon, Tue, Wed, Thu, Fri, Sat};

console.log(Days["Sun"] === 0); // true
console.log(Days["Mon"] === 1); // true
console.log(Days["Tue"] === 2); // true
console.log(Days["Sat"] === 6); // true

console.log(Days[0] === "Sun"); // true
console.log(Days[1] === "Mon"); // true
console.log(Days[2] === "Tue"); // true
console.log(Days[6] === "Sat"); // true

In fact, the above example will compile to:

var Days;
(function (Days) {<!-- -->
    Days[Days["Sun"] = 0] = "Sun";
    Days[Days["Mon"] = 1] = "Mon";
    Days[Days["Tue"] = 2] = "Tue";
    Days[Days["Wed"] = 3] = "Wed";
    Days[Days["Thu"] = 4] = "Thu";
    Days[Days["Fri"] = 5] = "Fri";
    Days[Days["Sat"] = 6] = "Sat";
})(Days || (Days = {<!-- -->}));
;
console.log(Days["Sun"] === 0); // true
console.log(Days["Mon"] === 1); // true
console.log(Days["Tue"] === 2); // true
console.log(Days["Sat"] === 6); // true
console.log(Days[0] === "Sun"); // true
console.log(Days[1] === "Mon"); // true
console.log(Days[2] === "Tue"); // true
console.log(Days[6] === "Sat"); // true

Manual assignment

We can also manually assign values to enumeration items. At this time, the increment step size of subsequent items that are not manually assigned is still 1:

enum Days {<!-- -->Sun = 7, Mon = 1, Tue, Wed, Thu, Fri, Sat};

console.log(Days["Sun"] === 7); // true
console.log(Days["Mon"] === 1); // true
console.log(Days["Tue"] === 2); // true
console.log(Days["Sat"] === 6); // true

In the above example, the enumeration items that have not been manually assigned a value will be incremented following the previous enumeration item. If the enumeration items that are not manually assigned are the same as those that are manually assigned, TypeScript will not notice this and is not recommended

enum Days {<!-- -->Sun = 3, Mon = 1, Tue, Wed, Thu, Fri, Sat};

console.log(Days["Sun"] === 3); // true
console.log(Days["Wed"] === 3); // true
console.log(Days[3] === "Sun"); // false
console.log(Days[3] === "Wed"); // true

In the above example, when incrementing to 3, the value of the previous Sun is repeated, but TypeScript does not report an error, resulting in Days[3] was first "Sun" and then overwritten by "Wed". The result of compilation is:

var Days;
(function (Days) {<!-- -->
    Days[Days["Sun"] = 3] = "Sun";
    Days[Days["Mon"] = 1] = "Mon";
    Days[Days["Tue"] = 2] = "Tue";
    Days[Days["Wed"] = 3] = "Wed";
    Days[Days["Thu"] = 4] = "Thu";
    Days[Days["Fri"] = 5] = "Fri";
    Days[Days["Sat"] = 6] = "Sat";
})(Days || (Days = {<!-- -->}));

Manually assigned enumeration items may not be numbers. In this case, type assertions need to be used to make tsc ignore type checking (the compiled js is still available):

enum Days {<!-- -->Sun = 7, Mon, Tue, Wed, Thu, Fri, Sat = <any>"S"};
var Days;
(function (Days) {<!-- -->
    Days[Days["Sun"] = 7] = "Sun";
    Days[Days["Mon"] = 8] = "Mon";
    Days[Days["Tue"] = 9] = "Tue";
    Days[Days["Wed"] = 10] = "Wed";
    Days[Days["Thu"] = 11] = "Thu";
    Days[Days["Fri"] = 12] = "Fri";
    Days[Days["Sat"] = "S"] = "Sat";
})(Days || (Days = {<!-- -->}));

Of course, manually assigned enumeration items can also be decimals or negative numbers. At this time, the increment step size of subsequent items that are not manually assigned is still 1:

enum Days {<!-- -->Sun = 7, Mon = 1.5, Tue, Wed, Thu, Fri, Sat};

console.log(Days["Sun"] === 7); // true
console.log(Days["Mon"] === 1.5); // true
console.log(Days["Tue"] === 2.5); // true
console.log(Days["Sat"] === 6.5); // true

Constant terms and calculated terms

There are two types of enumeration items: constant members and computed members.

The examples we gave above are all constant terms, a typical example of calculated terms:

enum Color {<!-- -->Red, Green, Blue = "blue".length};

In the above example, "blue".length is a calculated item. The above example will not report an error, but if the calculated item is immediately followed by an item that has not been manually assigned, then it will report an error because the initial value cannot be obtained:

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.

Enumeration members are treated as constants when the following conditions are met:

  • There is no initialization function and the previous enumeration members are constants. In this case, the value of the current enumeration member is the value of the previous enumeration member plus 1. The exception is the first enumeration element. If it has no initialization method, its initial value is 0.
  • **Enumeration members are initialized using constant enumeration expressions. **Constant enumeration expressions are a subset of TypeScript expressions that can be evaluated at compile time. An expression is a constant enumeration expression when it satisfies one of the following conditions:
    • numeric literal
    • Reference a previously defined constant enumeration member (which can be defined in a different enumeration type). If the member is defined in the same enumeration type, you can use an unqualified name to reference it.
    • Parenthesized constant enumeration expression
    • + , -, ~ unary operators applied to constant enumeration expressions
    • + , -, *, /, %, << , >>, >>>, &, |, ^ Binary operator, a constant enumeration expression is used as one of its operands. If the constant enumeration expression evaluates to NaN or Infinity, an error will be reported during the compilation phase.

In all other cases enumeration members are treated as values to be calculated.

Constant enumeration const enum

Constant enumerations are enumeration types defined using const enum:

const enum Directions {<!-- -->
    Up,
    Down,
    Left,
    Right
}

let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];

The difference between a constant enumeration and a normal enumeration is that it will be deleted during the compilation phase and cannot contain calculated members. If calculated members are included, an error will be reported during the compilation phase:

The compilation result of the above example is:

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.

External enumeration declare enum

Ambient Enums are enumeration types defined using declare enum:

declare enum Directions {<!-- -->
    Up,
    Down,
    Left,
    Right
}

let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];

As mentioned before, the types defined by declare will only be used for compile-time checking and will be deleted from the compilation results. The compilation result of the above example is:

var directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];

External enumerations, like declaration statements, often appear in declaration files.

Note: It is also possible to use declare and const simultaneously:

declare const enum Directions {<!-- -->
    Up,
    Down,
    Left,
    Right
}

let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];

Compilation result:

var directions = [0 /* Up */, 1 /* Down */, 2 /* Left */, 3 /* Right */];
syntaxbug.com © 2021 All Rights Reserved.