TL;DR: In this article, we will look into enums, what they are and how they are used in TypeScript. We will also use sample code to illustrate the points made.
Here are some reasons why enums are very useful in TypeScript:
- With enums you can create constants that you can easily relate to, making constants more legible
- TypeScript enums also provide a certain flexibility that we previously had only in languages like Java. This flexibility makes it easy to express and document our intentions and use cases easily.
Enums are defined with the enum keyword like this:
There are three types of TypeScript enums namely:
- Numeric enums
- String enums
- Heterogenous enums
By default, TypeScript enums are number based. This means that they can store string values as numbers. Numbers and any other type that is compatible with it can be assigned to an instance of the enum. Let’s say we want to store days in the weekend. The representing enum in TypeScript can look something like this:
In the code block above, we have an enum we call
Weekend. The enum has three values namely:
Sunday. In TypeScript, just like in some other languages, enum values start from zero and increase by one for each member. They will be stored like this:
We see that enums are always assigned numbers for storage, the value always takes the numeric value of zero, although we can customize the storage values with our own logic.
In TypeScript, we are allowed to dictate the first numeric value of our enumerations. Using the weekend days example above, we can initialize the first numeric value like this:
The above code block will store
Friday as 1,
Saturday as 2 and
Sundayas 3. If we add a number to the first member, we still get sequential incrementation by one for the rest of the members. However, we have the power to dictate that we do not want a sequential trail by giving them any numerical value. The code block below is semantic and works in TypeScript:
Just like other data types in TypeScript, we can use enums as function parameters or return types, like this:
In the code block above, we declared a
Weekend enum. We then declared a
getDate function that takes the input
Day that returns a
Weekendenum. In the function, we check for some condition that now returns an enum member.
So far we have only looked at enums where the member values are numbers. In TypeScript, your enum members can also be string values. String enums are vital and easy to deal with for the purpose of readability during error logging and debugging because of their meaningful string values.
It can then be used to compare strings in conditional statements like this:
In the example above, we have defined a string enum,
Weekend just like the numeric enum we had above, but this time with the enum values as strings. The obvious difference between numeric and string enums is that numeric enum values are mostly sequentially incremented automatically, while string enum values are not incremented but rather each value is initialized independently.
TypeScript also allows for a mixture of both strings and numbers, called heterogeneous enum values:
The value of a numeric enum can either be constant or evaluated, just like any other number data type in TypeScript. You can define or initialize your numeric enum with a computed value:
Rule #1 — when enums include a mixture of computed and constant members, the enum members that are not initialized either come first or must come after other initialised members with numeric constants.
Ignoring this rule above gives an initializer error — if you see that, remember to rearrange the enum members accordingly.
If you want to boost the performance of your numeric enums you can declare them as a constant. Let us use our weekend example to illustrate:
Weekend and looks up
Weekend.Saturday. For optimal performance at runtime, you can make the enum a constant instead, like this:
preserveConstEnums and it will still generate the
TypeScript enums support reverse mapping, which simply means that just as we have access to the value of an enum member, we also have access to the enum name itself. A sample of our first demonstration is used to portray this below:
In the code block above,
Weekend.Saturday will return 2 and then
Weekend["Saturday"] will also return 2 but interestingly, due to reverse mapping
Weekend will return its member name
Saturday. This is because of reverse mapping. We can see a simple way TypeScript interprets reverse mapping with a log command:
If you run this in a console, you will see this output:
The objects contain the enums appearing both as values and as names, just as TypeScript intended. This shows the potency of reverse mapping in TypeScript.
There are places and suitable use cases where it’s optimal and very efficient to use enums
- Enums can be used inside array initialisations just as other TypeScript data types
Here is a quick example:
- Enums should ideally be used in situations where there are distinct values that can be seen as constants, like seven days of the week:
- Enums can also be used in places where strings or constants need to be represented in a variable.
TypeScript enums are not to be used in the following places:
- When you plan to re-assign or change the enum member values, enums are type-safe and therefore would return compile errors on re-assignment
- When you want to record dynamic values, enums are best suited for finite items and the general idea behind it was to help create a user-defined constants system
- Enums cannot be used as variables, doing so would return errors
We have been able to take a good look at enums in TypeScript, their types and properties. We also saw the syntax and practical examples of how they are used. We saw other important enums aspects like constants in enums, computed enums and even reverse mapping. It is noteworthy that for string enums, reverse mapping is not supported. Also for the heterogeneous ones, it is only supported for numeric type members but not for string type members. Happy coding!
LogRocket is a frontend logging tool that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.