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.
enum Direction { Up = 1, Down, Left, Right, } console.log(Direction);
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.
enum Direction { Up, Down, Left, Right, } console.log(Direction);
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 Direction { Up=1, Down=3, Left=6, Right=10, } console.log(Direction);
We can also use an enum as a function type or return type, which we can see in the below example.
enum AppStatus { ACTIVE, INACTIVE, ONHOLD } function checkStatus(status: AppStatus): void { console.log(status); } checkStatus(AppStatus.ONHOLD);
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:
enum AppStatus { ACTIVE = 'ACT', INACTIVE = 'INACT', ONHOLD = 'HLD', ONSTOP = 'STOP' } function checkStatus(status: AppStatus): void { console.log(status); } checkStatus(AppStatus.ONSTOP);
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.
The heterogeneous enums are enums, which contains both string and numeric values. But it is advised that you don't do this unless there is a need to take advantage of JavaScript runtime behavior.
enum AppStatus { ACTIVE = 'Yes', INACTIVE = 1, ONHOLD = 2, ONSTOP = 'STOP' } console.log(AppStatus.ACTIVE); console.log(AppStatus.ONHOLD);
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.
// Name.Abhishek is constant: enum Name { Abhishek } console.log(Name);
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.
// All enum members in 'Name' and 'Profile' are constant. enum Name { Abhishek, Ravi, Ajay } enum Profile { Engineer=1, Leader, Businessman }
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.
enum Weekend { Friday = 1, Saturday = getDate('Dominoz'), Sunday = Saturday * 40 } function getDate(day : string): number { if (day === 'Dominoz') { return 3; } } console.log(Weekend.Saturday); console.log(Weekend.Sunday);
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.
enum Weekend { Friday = 1, Saturday, Sunday } console.log(Weekend.Saturday); console.log(Weekend["Saturday"]); console.log(Weekend[3]);
Enums are the real objects which exist at runtime. We can understand it from the below example.
enum E { A, B, C }
It can actually be passed around to functions, which we can see in the below example.
function f(obj: { A: number }) { return obj.A; } // Works, since 'E' has a property named 'A' which is a number. f(E);
We can use ambient enums for describing the shape of already existing enum types.
Declare enum Enum{ X=1, Y, Z=2 }
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.