Enums stands for Enumerations. Enums are a new data type supported in TypeScript. It is used to define the set of named constants, i.e., a collection of related values. TypeScript supports both numeric and string-based enums. We can define the enums by using the enum keyword.
Enums are useful in TypeScript because of the following:
There are three types of Enums in TypeScript. These are:
Numeric enums are number-based enums, which store values as numbers. It means we can assign the number to an instance of the enum.
In the above example, we have a numeric enum named Direction. Here, we initialize Up with 1, and all of the following members are auto-incremented from that point. It means Direction.Up has the value 1, Down has 2, Left has 3, and Right has 4.
According to our need, it also allows us to leave off the initialization of enumeration. We can declare the enum without initialization as below.
Here, Up have the value 0, and all of the following members are auto-incremented from that point. It means Direction.Up has the value 0, Down has 1, Left has 2, and Right has 3. The auto-incrementing behavior is useful when there is no need to care about the member values themselves. But each value must be distinct from other values in the same enum.
In TypeScript enums, it is not necessary to assign sequential values to enum members always. We can provide any values to the enum members, which looks like the below example.
Enum as a function argument
We can also use an enum as a function type or return type, which we can see in the below example.
In the above example, we have declared an enum AppStatus. Next, we create a function checkStatus() that takes an input parameter status which returns an enum AppStatus. In the function, we check for the type of status. If status name matches, we get the matched enum member.
Here, we can see that the value printed '2' in the last statement is not much useful in most of the scenarios. That's why it is preferred and recommended to use string-based enums.
String enums are a similar concept to numeric enums, except that the enum has some subtle runtime differences. In a string enum, each enum values are constant-initialized with a string literal, or with another string enum member rather than numeric values.
String enums do not have auto-incrementing behavior. The benefits of using this enum is that string enums provides better readability. If we were debugging a program, string enums allow us to give a meaningful and readable value when our code runs, independent of the name of the enum member itself.
Consider the following example of a numeric enum, but it is represented as a string enum:
In the above example, we have declared a string enum AppStatus with the same values as the numeric enum above. But string enum is different from numeric enum where string enum values are initialized with string literals. The difference between these enums is that the numeric enum values are auto-incremented, whereas string enum values need to be initialized individually.
Computed and constant members
We know that each enum members has a value associated with it. These values can be either constant or computed. We can consider enum member as constant if:
1. It is the first member of the enum and has no initializer value. In this case, it is assigned the value 0.
2. It has no initializer value, and the preceding enum member is a numeric constant. In this case, the value of the current enum member will be the value of the preceding enum member plus one.
In TypeScript, we can say that an expression is a constant enum expression if it is:
In all other cases, the enum member is considered computed. The following enum example includes enum members with computed values.
TypeScript enums also support reverse mapping. It means we can access the value of an enum member, and also can access a member name from its value. We can understand the reverse mapping from the below example.
Note: The string enum does not support reverse mapping.
Enums at runtime
Enums are the real objects which exist at runtime. We can understand it from the below example.
It can actually be passed around to functions, which we can see in the below example.
We can use ambient enums for describing the shape of already existing enum types.
There is mainly one difference between ambient and non-ambient enums. In regular enums, members that do not have an initializer is considered as constant if its preceding enum member is considered constant. But, an ambient (and non-const) enum member that does not have initializer is always considered computed enums.