A Typescript enum is a special data type that allows developers to create a variable that can take a set of predefined constants. In this article, we will explore the enum keyword in TypeScript and see how to use it to write readable and maintainable code.
Table of Contents
Introduction to Enums in TypeScript
A Typescript enum allows us to define the collection of related values to be used by name. They make the code easier to read and maintain. You may use them to limit a variable’s values to a small set of possible values.
Javascript does not support enums. Hence typescript creates runtime artifacts to support them ( Except const enum
which do not have a runtime artifact).
TypeScript provides both numeric and string-based enums.
Where to use Enum
There are many use cases where the enum is very useful. For Example, directions can only take four values. North
, east
, south
, and west
. We can use codes like N
, E
, S
, & W
to represent them. Another example is the days of the week, which you can represent as 1 to 7. Traffic lights are another example where a variable takes finite values like green
, yellow
, & red
The enums make the code more readable and less error-prone. For Example, using codes like 1
,2
, etc., to represent days of the week is less readable compared to the Weekdays.Sunday
, Weekdays.Monday
.
It also makes it easier to change the values in the future without breaking the code. For Example, if you decide to use the number instead of N
, E
, S
, & W
for directions, you can do so easily.
You can also use the string literal or numeric literal types and the union types. These literal types do not have runtime artifacts and hence may be preferable over the Enum
. Choose one based on your preference.
Typescript Enum Examples
We define a enum
by using the enum
keyword, followed by the name of the enum (Weekdays). Then we define the members of the enum. Each member must have a name (Monday, Tuesday, etc.) and value (1,2, etc.). A comma separates the members. The trailing comma is allowed & ignored. If we omit the value of a member, then the TypeScript increments the value of the preceding member by one and assigns it to the current member: If the preceding member is a string, an error is thrown.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | enum Weekdays { Monday = 1, Tuesday = 2, Wednesday = 3, Thursday = 4, Friday = 5, Saturday = 6, Sunday = 7 } console.log(Weekdays.Monday) //1 console.log(Weekdays["Monday"]) //1 console.log(Weekdays["1"]) //Monday let holiday=Weekdays.Sunday; console.log(holiday); //7 console.log(typeof Weekdays) //object console.log(typeof holiday) //number |
In the above example, we have initialized the values. But If we omit the values, then the typescript initializes it with the values starting from 0
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | enum Weekdays { Monday, //Initialized with zero Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday } console.log(Weekdays.Monday) //0 console.log(Weekdays["Monday"]) //0 console.log(Weekdays["1"]) //Tuesday |
You will get nice intellisense help when you use them.
we can use the quote in the names of enum members, as shown below
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | enum Weekdays { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" } console.log(Weekdays.Monday) //0 console.log(Weekdays["Monday"]) //0 console.log(Weekdays["1"]) //Tuesday |
1 2 3 4 5 6 7 8 9 10 11 | enum Weekdays { Monday = 1, Tuesday = 2, Wednesday = 3, Thursday = 4, Friday = 5, Saturday = 6, Sunday = 7 } |
Javascript does not support enums. The Typescript transpiles the above TypeScript code to JavaScript as follows.
1 2 3 4 5 6 7 8 9 10 11 12 13 | "use strict"; var Weekdays; (function (Weekdays) { Weekdays[Weekdays["Monday"] = 1] = "Monday"; Weekdays[Weekdays["Tuesday"] = 2] = "Tuesday"; Weekdays[Weekdays["Wednesday"] = 3] = "Wednesday"; Weekdays[Weekdays["Thursday"] = 4] = "Thursday"; Weekdays[Weekdays["Friday"] = 5] = "Friday"; Weekdays[Weekdays["Saturday"] = 6] = "Saturday"; Weekdays[Weekdays["Sunday"] = 7] = "Sunday"; })(Weekdays || (Weekdays = {})); |
enum Types in Typescript.
The Typescript enums can store a String constant, numerical constant or mix of both. There are three enum types Typescript.
- Numeric enums in TypeScript
- String enums in TypeScript
- Heterogeneous enums (both string & numbers) in TypeScript
Typescript enum does not support other types other than numbers and strings as its member.
Numeric Enums in TypeScript
The Numeric enum
stores the values as a number. You can define a enum
without assigning a value, as shown below. The Typescript automatically assigns the numeric values starting from 0
. You can see from the following example that Car,plane & train
get the value 0,1 & 2
respectively.
1 2 3 4 5 6 7 8 9 10 11 12 13 | enum VehicleType{ Car, //0 Plane, //1 Train //2 } console.log(VehicleType.Plane) //1 console.log(VehicleType["Plane"]) //1 VehicleType //Get the name from the Enum Member. console.log(VehicleType[VehicleType.Plane]) //Plane |
You can provide a starting number, as shown in the example below. The plane & train automatically get ( 3 & 4). i.e., the next available number.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | enum VehicleType{ Car=2, Plane, Train } console.log(VehicleType) //Output { '2': 'Car', '3': 'Plane', '4': 'Train', Car: 2, Plane: 3, Train: 4 } |
The following example shows how to use an enum as a return type
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | enum VehicleType{ Car=1, Plane, Train } function getVehicle(name: string): VehicleType| undefined { if ( name === 'ferrari') { return VehicleType.Car; } else { return undefined } } console.log(getVehicle('ferrari')); //output 1 |
String Enums in TypeScript
We can also use strings instead of numbers as values. The only difference is that we must initialize the values.
1 2 3 4 5 6 7 8 9 10 | enum VehicleType{ Car="Car", Plane="Plane", Train="Train" } console.log(VehicleType.Car); //Car console.log(VehicleType["Car"]); //Car |
Use it in a function as shown below
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | enum VehicleType{ Car="C", Plane="P", Train="T" } let vehicle = VehicleType.Plane printName(vehicle); function printName(val: VehicleType) { switch (val) { case VehicleType.Car: console.log("It is a Car"); break; case VehicleType.Plane: console.log("It is a Plane."); break; case VehicleType.Train: console.log("It is a Train."); break; default: console.log("No such vehicle"); break; } } |
1 2 3 4 5 6 7 8 | const value:VehicleType=VehicleType.Car ; if (value === VehicleType.Car || value === VehicleType.Train){ console.log('Please choose a plane'); console.log(value); } |
Heterogeneous Enums in TypeScript
The Heterogeneous enums allow a mix of both string & number.
If you do not provide an initial value, the typescript will automatically assign a number incremented by one from the previous value. If the previous value is a string
, it will result in an error.
For Example, Train
gets the value 2 here
1 2 3 4 5 6 7 8 9 10 11 12 | enum VehicleType{ Car="C", Plane=1, Train } console.log(VehicleType) //OUTPUT { '1': 'Plane', '2': 'Train', Car: 'C', Plane: 1, Train: 2 } |
This results in an error Enum member must have initializer
.
1 2 3 4 5 6 7 | enum VehicleType{ Car="C", Plane, //Error Enum member must have initializer. Train } |
Computed enum in TypeScript
We can also include the members with computed values. They are allowed only in the case of numeric enums. The string & heterogeneous enums do not support computed enums.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | enum VehicleType{ Car=1, Plane, Train= 5 + VehicleType.Plane } console.log(VehicleType) //OUTPUT { '1': 'Car', '2': 'Plane', '7': 'Train', Car: 1, Plane: 2, Train: 7 } |
You can use any function. The following code generates a random number from 10 to 20. Hence you will get a different number for the train on each run.
1 2 3 4 5 6 7 8 9 10 11 12 13 | enum VehicleType{ Car=1, Plane, Train= getRandomNumberBetween(10,20) } console.log(VehicleType) function getRandomNumberBetween(min,max){ return Math.floor(Math.random()*(max-min+1)+min); } |
Reverse Mapping of Enum
The Typescript generates a reverse mapping for numeric enum members. For Example
1 2 3 4 5 6 7 | enum VehicleType { Car, Plane, Train } |
The Compiler emits the following code. As you can see from the code, the compile generates both the forward mapping (VehicleType["Car"] = 0
) and reverse mapping (VehicleType[0] = "Car";
) is a single statement VehicleType[VehicleType["Car"] = 0] = "Car";
1 2 3 4 5 6 7 8 | var VehicleType; (function (VehicleType) { VehicleType[VehicleType["Car"] = 0] = "Car"; VehicleType[VehicleType["Plane"] = 1] = "Plane"; VehicleType[VehicleType["Train"] = 2] = "Train"; })(VehicleType || (VehicleType= {})); |
This means you can get the value of an enum from its name VehicleType[“Car”] or get the name of the enum from its value (VehicleType[0]
)
1 2 3 4 5 | console.log(VehicleType.Car) //0 console.log(VehicleType["Car"]) //0 console.log(VehicleType[0]) //Car |
The reverse mapping for numeric enum members works even in Heterogeneous Enums with number values, as shown below. The string enum members do not support reverse mappings.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | enum VehicleType{ Car="C", Plane="P", Train=10 } console.log(VehicleType.Plane) //P console.log(VehicleType["Plane"]) //P console.log(VehicleType["P"]) //undefined //ERROR console.log(VehicleType.Train) //10 console.log(VehicleType["Train"]) //10 console.log(VehicleType[10]) //Train //Works |
Transpiled code.
1 2 3 4 5 6 7 8 | var VehicleType; (function (VehicleType) { VehicleType["Car"] = "C"; //No Reverse Mapping for strings VehicleType["Plane"] = "P"; // VehicleType[VehicleType["Train"] = 10] = "Train"; //Reverse Mapping })(VehicleType|| (VehicleType= {})); |
Const Enums
If an enum is prefixed with the keyword const, it doesn’t get transpiled. i.e., no javascript code is emitted. Instead, the compiler will replace the value of its member directly in the transpiled code, wherever they are used.
Since they do not have runtime representation, you cannot use computed members.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | const enum VehicleType{ Car, Plane, Train } console.log(VehicleType.Car) //0 console.log(VehicleType.Plane) //1 console.log(VehicleType["Car"]) //0 console.log(VehicleType["Plane"]) //1 console.log(VehicleType[0]) //ERROR console.log(VehicleType[1]) //ERROR //Transpiled Code // console.log(0 /* Car */); //0 //VehicleType.Car is replaced with its value 0 console.log(1 /* Plane */); //1 console.log(0 /* "Car" */); //0 console.log(1 /* "Plane" */); //1 console.log(VehicleType[0]); //ERROR console.log(VehicleType[1]); //ERROR |
Type Checking of enums
The Typescript performs a loose type checking when the number of members is involved. In the following example, someFn
accepts VehicleType
as an argument. But someFn
also accepts any number. Invoking someFn(100) with 100 as an argument does not throw any errors.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | enum VehicleType { Car, Plane, Train } function someFn(val:VehicleType) { console.log(val); } someFn(VehicleType.Car) //OK someFn(100) //NO ERROR someFn("Test") //Error someFn("C") //Error someFn("Car") //Error |
But in the case of string
enums
it works as expected.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | enum VehicleType{ Car="C", Plane="P", Train="T" } function someFn(val:VehicleType) { console.log(val); } someFn(VehicleType.Car) //OK someFn(100) //Error someFn("Test") //Error someFn("C") //Error someFn("Car") //Error |
The loose checking of type exists when you mix numbers & strings as in Heterogeneous enums.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | enum VehicleType{ Car="C", Plane="P", Train=10 } function someFn(val:VehicleType) { console.log(val); } someFn(VehicleType.Car) //OK someFn(100) //NO ERROR someFn("Test") //Error someFn("C") //Error someFn("Car") //Error |