The author choosesCOVID-19 Relief FundAs aWrite for DOnationsPart of the program accepts donations.

Introduction to the

In TypeScript, enumerations _ or enumerated types are data structures of constant length that hold a set of constant values. Each of these constant values is called an _ member of the enumeration. Enumerations are useful when setting properties or only a certain number of possible values. A common example is the suit value of a card in a deck of playing cards. Every card drawn is either clubs, diamonds, hearts, or spades; Outside of these four cards, there are no possible suit values, and these possible values cannot be changed. Because of this, an enumeration would be an efficient and clear way to describe the possible suits of a card.

While most of TypeScript’s features are useful for throwing errors during compilation, enumerations are also useful as data structures that hold constants for your code. TypeScript translates enums into JavaScript objects in the final code emitted by the compiler. Because of this, you can use enumerations to make your code base more readable, because you can group multiple constant values in the same data structure, and also make your code more type-safe, rather than just putting different const variables together.

This tutorial explains the syntax for creating enumerated types, the JavaScript code the TypeScript compiler creates under the shell, how to extract enumerated object types, and the use case for enumerations in game development involving bit flags.

The premise condition

To follow this tutorial, you will need.

  • An environment in which you can execute TypeScript programs to follow the example. To set this up on your local machine, you need the following.
    • Install Both Node and NPM (or YARN) to run a development environment that handles typescript-related packages. This tutorial was tested on Node.js version 14.3.0 and NPM version 6.14.5. To install on macOS or Ubuntu 18.04, follow the steps in “How to Install Node.js on macOS and Create a Local Development Environment” or follow the “Installing with PPA” section in “How to Install Node.js on Ubuntu 18.04”. The same applies if you use Windows Subsystem for Linux (WSL).
    • In addition, you will need to install the TypeScript compiler on your machine.tsc). To do this, please refer toThe official TypeScript website.
  • If you don’t want to create a TypeScript environment on your local machine, you can use the official TypeScript Playground to follow along.
  • You need to have a good knowledge of JavaScript, especially ES6+ syntax such as structuring, rest parameters, and import/export. If you need more information on these topics, we recommend reading our how to Program in JavaScript series.
  • This tutorial references various aspects of typescript-enabled text editors and displays inline errors. This isn’t necessary to use TypeScript, but it does allow you to take advantage of TypeScript’s features. To get these benefits, you can use a text editor like Visual Studio Code, which has full support for TypeScript out of the box. You can also try these advantages in TypeScript Playground.

All of the examples shown in this tutorial were created using TypeScript version 4.2.3.

Create enumerations in TypeScript

In this section, you’ll walk through an example of declaring an _ numeric enumeration _ and an _ string enumeration _.

Enumerations in TypeScript are typically used to represent a certain number of options for a given value. The data is arranged in a set of key/value pairs. Although the key must be a string, just like a normal JavaScript object, enumeration members’ values are usually auto-incrementing numbers, primarily used to distinguish one member from another. Enumerations with only numeric values are called _ numeric enumerations _.

To create a numeric enumeration, use the enum keyword, followed by the name of the enumeration. Then create a brace ({}) block in which you will specify enumerators, like this.

enum CardinalDirection {
  North = 1,
  East,
  South,
  West,
};
Copy the code

In this example, you are creating an enumeration called CardinalDirection that has one member representing each CardinalDirection. Enumerations are an appropriate data structure choice to hold these options, because values always have only four options: north, South, East, and west.

You use the number 1 as the value for the first member of your CardinalDirection enumeration. This specifies the number 1 as the value of North. However, you do not assign values to other members. This is because TypeScript automatically sets the remaining members to the value of the previous member plus one. Cardinaldirection. East has the value 2, Cardinaldirection. South has the value 3, and Cardinaldirection. West has the value 4.

This behavior applies only to numeric enumerations where each member has only numeric values.

You can also ignore setting the enumerator’s value entirely.

enum CardinalDirection {
  North,
  East,
  South,
  West,
};
Copy the code

In this case, TypeScript sets the first member to 0 and then automatically sets the other members based on that member, incrementing each member by 1. This results in the same code as below.

enum CardinalDirection {
  North = 0,
  East = 1,
  South = 2,
  West = 3};Copy the code

The TypeScript compiler assigns numbers to enumerators by default, but you can override this to make it a string enumeration. Each member of these enumerations has a string value; These enumerations are useful when the value needs to have some kind of human-readable meaning, such as when you need to read the value later in a log or error message.

You can declare that an enumeration member has a string value with the following code.

enum CardinalDirection {
  North = 'N',
  East = 'E',
  South = 'S',
  West = 'W'
}
Copy the code

Each direction now has a letter value indicating which direction they hook to.

Having covered the declarative syntax, you can now examine the underlying JavaScript to learn more about how enumerations behave, including the bidirectional nature of key/value pairs.

A bidirectional enumerator

Enumerations are translated into JavaScript objects when TypeScript is compiled. However, enumerations have several characteristics that make them different from objects. They provide a more stable data structure to store constant members than traditional JavaScript objects, and they also provide bidirectional references to enumeration members. To illustrate this, this section shows you how TypeScript compiles enumerations in your final code.

Take the string enumeration you created in the previous section.

enum CardinalDirection {
  North = 'N',
  East = 'E',
  South = 'S',
  West = 'W'};Copy the code

When compiled into JavaScript using the TypeScript compiler, this becomes the following code.

"use strict";
var CardinalDirection;
(function (CardinalDirection) {
    CardinalDirection["North"] = "N";
    CardinalDirection["East"] = "E";
    CardinalDirection["South"] = "S";
    CardinalDirection["West"] = "W";
})(CardinalDirection || (CardinalDirection = {}));
Copy the code

In this code, the “Use strict” string initiates strict mode, which is a stricter version of JavaScript. TypeScript then creates a variable CardinalDirection that has no value. The code then contains an immediate function expression (IIFE) that takes the CardinalDirection variable as an argument and also sets its value to an empty object ({}) if it has not already been set.

Inside the function, once CardinalDirection is set to an empty object, the code assigns multiple attributes to that object.

"use strict";
var CardinalDirection;
(function (CardinalDirection) {
    CardinalDirection["North"] = "N";
    CardinalDirection["East"] = "E";
    CardinalDirection["South"] = "S";
    CardinalDirection["West"] = "W";
})(CardinalDirection || (CardinalDirection = {}));
Copy the code

Note that each property is a member of your original enumeration, and its value is set to the enumeration’s member value.

For string enumerations, this is the end of the process. But next you’ll try the same thing with numeric enumerations from the previous section.

enum CardinalDirection {
  North = 1,
  East,
  South,
  West,
};
Copy the code

This results in the following code, with the highlighted section added.

"use strict";
var CardinalDirection;
(function (CardinalDirection) {
    CardinalDirection[CardinalDirection["North"] = 1] = "North";
    CardinalDirection[CardinalDirection["East"] = 2] = "East";
    CardinalDirection[CardinalDirection["South"] = 3] = "South";
    CardinalDirection[CardinalDirection["West"] = 4] = "West";
})(CardinalDirection || (CardinalDirection = {}));
Copy the code

In addition to each member of the enumeration becoming an attribute of the object (CardinalDirection[“North”] = 1]), the enumeration also creates a key for each number and assigns strings as values. In the case of North, CardinalDirection[“North”] = 1 returns the value 1, and CardinalDirection[1] = “North” assigns the value “North” to the key “1”.

This creates a bidirectional relationship between the names of numeric members and their values. To test this, record the following.

console.log(CardinalDirection.North)
Copy the code

This returns the value of the key “North”.

Output1
Copy the code

Next, run the following code to reverse the direction of the reference.

console.log(CardinalDirection[1])
Copy the code

The output will be:.

Output"North"
Copy the code

To illustrate the final object that represents the enumeration, log the entire enumeration to the console.

console.log(CardinalDirection)
Copy the code

This displays two sets of key/value pairs that have a bidirectional effect.

Output{
  "1": "North",
  "2": "East",
  "3": "South",
  "4": "West",
  "North": 1,
  "East": 2,
  "South": 3,
  "West": 4
} 
Copy the code

With an understanding of how enumerations work in TypeScript, you will now move on to using enumerations to declare types in your code.

Use enumerations in TypeScript

In this section, you’ll try the basic syntax of specifying enumeration members as types in TypeScript code. This can be done in the same way as primitive type declarations.

To use your CardinalDirection enumeration as a variable type in TypeScript, you can use the enumeration’s name, as shown in the highlighted code below.

enum CardinalDirection {
  North = 'N',
  East = 'E',
  South = 'S',
  West = 'W'};const direction: CardinalDirection = CardinalDirection.North;
Copy the code

Notice that you are setting the variable to enumeration as its type.

const direction: CardinalDirection = CardinalDirection.North;
Copy the code

You also set the value of the variable to a member of the enumeration, in this case Cardinaldirection.north. You can do this because enumerations are compiled as JavaScript objects, so they have a value representation in addition to being types.

If you pass a value that is incompatible with the enumeration type of your direction variable, like this.

const direction: CardinalDirection = false;
Copy the code

The TypeScript compiler displays error 2322.

OutputType 'false' is not assignable to type 'CardinalDirection'. (2322)
Copy the code

Therefore, direction can only be set to a member of the CardinalDirection enumeration.

You can also set the type of your variable to a specific enumerator.

enum CardinalDirection {
  North = 'N',
  East = 'E',
  South = 'S',
  West = 'W'};const direction: CardinalDirection.North = CardinalDirection.North;
Copy the code

In this case, the variable can only be assigned to the North member of the CardinalDirection enumeration.

If your enumerators have numeric values, you can set the values of your variables to those numeric values as well. For example, consider enumeration.

enum CardinalDirection {
  North = 1,
  East,
  South,
  West,
};
Copy the code

You can set the value of a variable of type CardinalDirection to 1.

const direction: CardinalDirection = 1;
Copy the code

This is possible because 1 is the value of the North member of your CardinalDirection enumeration. This only works for the numeric members of the enumeration, and it relies on compiled JavaScript’s bidirectional relationship to the numeric enumeration members, which was covered in the previous section.

Now that you’ve tried declaring variable types with enumerated values, the next section demonstrates a special way to manipulate enumerations: extract the underlying object type.

Extract the object type of the enumeration

In the previous section, you found that enumerations are not just a type-level extension of JavaScript, but have real values. This also means that the enumeration data structure itself has a type, which you have to take into account if you want to set up a JavaScript object that represents an enumeration instance. To do this, you will need to extract the type of the enumeration object itself.

Given your CardinalDirection enumeration.

enum CardinalDirection {
  North = 'N',
  East = 'E',
  South = 'S',
  West = 'W'};Copy the code

Try to create an object that matches your enumeration, as follows.

enum CardinalDirection {
  North = 'N',
  East = 'E',
  South = 'S',
  West = 'W'};const test1: CardinalDirection = {
  North: CardinalDirection.North,
  East: CardinalDirection.East,
  South: CardinalDirection.South,
  West: CardinalDirection.West,
}
Copy the code

In this code, test1 is an object of type CardinalDirection, and the value of this object includes all the members of the enumeration. However, the TypeScript compiler displays error 2322.

OutputType '{ North: CardinalDirection; East: CardinalDirection; South: CardinalDirection; West: CardinalDirection; }' is not assignable to type 'CardinalDirection'.
Copy the code

The reason for this error is that the CardinalDirection type represents the combined type of all enumerators, not the type of the enumeration object itself. You can extract the typeof an object by using typeof before enumerating the name. Check the highlighting code below.

enum CardinalDirection {
  North = 'N',
  East = 'E',
  South = 'S',
  West = 'W'};const test1: typeof CardinalDirection = {
  North: CardinalDirection.North,
  East: CardinalDirection.East,
  South: CardinalDirection.South,
  West: CardinalDirection.West,
}
Copy the code

The TypeScript compiler will now compile your code correctly.

This section shows specific ways to extend the use of enumerations. Next, you’ll examine a use case where enumeration is appropriate: bitflags in game development.

Use bitflags in TypeScript enumerations

In the final section of this tutorial, you’ll walk through the actual use cases enumerated in TypeScript. A sign.

Bit-flags are a way of representing different Boolean-like options in a single variable by using bitwise operations. To do this, each flag must use exactly one 32-bit bit, because this is the maximum JavaScript allows for bitwise manipulation. The biggest 32-bit number is 2147483647, 1111111111111111111111111111111 in binary, so you have the mark of 31 May.

Imagine that you are building a game where players may have different skills, such as SKILL_A, SKILL_B, SKILL_C. To make sure your program knows when a player has a skill, you can make some flags that can be turned on or off depending on the player’s state.

Each skill flag is given a binary value using the following pseudocode.

SKILL_A = 0000000000000000000000000000001
SKILL_B = 0000000000000000000000000000010
SKILL_C = 0000000000000000000000000000100
Copy the code

Now you can through the use of an operator | (OR), and the players all current skills are stored in a variable.

playerSkills = SKILL_A | SKILL_B
Copy the code

In this case, using the | operator to the player’s marks and 0000000000000000000000000000001, 0000000000000000000000000000010, That could produce 0000000000000000000000000000011 on behalf of the players have two kinds of skills.

You can also add more skills.

playerSkills |= SKILL_C
Copy the code

This will generate 0000000000000000000000000000111, said the player owns all three skills.

You can also use a combination of the bitwise operators & (AND) AND ~ (NOT) to remove a skill.

playerSkills &= ~SKILL_C
Copy the code

Then, to check if the player has a particular skill, you can use the bitwise operator & (AND).

hasSkillC = (playerSkills & SKILL_C) == SKILL_C
Copy the code

If the player does not have SKILL_C, the (playerSkills & SKILL_C) section is evaluated as 0. Otherwise, (playerSkills & SKILL_C) to assess the exact value for you to test your skill, in this case is SKILL_C (0000000000000000000000000000010). This way you can test that the value of the assessment is the same as the value of the skill you are testing.

Since TypeScript allows you to set the values of enumerations to integers, you can store these flags as an enumeration.

enum PlayerSkills {
  SkillA = 0b0000000000000000000000000000001,
  SkillB = 0b0000000000000000000000000000010,
  SkillC = 0b0000000000000000000000000000100,
  SkillD = 0b0000000000000000000000000001000};Copy the code

You can use the prefix 0b to represent binary numbers directly. If you don’t want to use such a large binary representation, you can use the bitwise operator << (left shift).

enum PlayerSkills {
  SkillA = 1 << 0,
  SkillB = 1 << 1,
  SkillC = 1 << 2,
  SkillD = 1 << 3};Copy the code

1 < < 0 will evaluate 0 b0000000000000000000000000000001, 1 < < 1 to 0 b0000000000000000000000000000010, 1 < < 2 to 0 b0000000000000000000000000000100, 1 < < 3 to 0 b0000000000000000000000000001000.

Now you can declare your playerSkills variable like this.

let playerSkills: PlayerSkills = PlayerSkills.SkillA | PlayerSkills.SkillB;
Copy the code

** Note: ** You must explicitly set the playerSkills variable’s type to playerSkills, otherwise TypeScript will infer that it is of type number.

To add more skills, you will use the following syntax.

playerSkills |= PlayerSkills.SkillC;
Copy the code

You can also remove a skill.

playerSkills &= ~PlayerSkills.SkillC;
Copy the code

Finally, you can use your enumeration to check if the player has any given skill.

const hasSkillC = (playerSkills & PlayerSkills.SkillC) === PlayerSkills.SkillC;
Copy the code

While bitmarks are still used under the hood, this solution provides a more readable and organized way to display data. It also makes your code more type-safe by storing binary values as constants in enumerations and throwing errors when playerSkills variables don’t match bit flags.

conclusion

Enumerations are a common data structure in most languages that provide type systems, and TypeScript is no exception. In this tutorial, you create and use enumerations in TypeScript, while working through more advanced scenarios such as extracting the object types of enumerations and using bitflags. With enumerations, you can make your code base more readable while organizing constants into a data structure rather than leaving them in global space.

For more tutorials on TypeScript, check out our How To Code in TypeScript series pages.