Enums
Enums
An enum is a way to organize a collection of related values. Many other programming languages (C/C#/Java) have an enum
data type but JavaScript does not. However, TypeScript does. Here is an example definition of a TypeScript enum:
These enums values are number
s so I'll call them Number Enums from hence forth.
Number Enums and Numbers
TypeScript enums are number based. This means that numbers can be assigned to an instance of the enum, and so can anything else that is compatible with number
.
Number Enums and Strings
Before we look further into enums let's look at the JavaScript that it generates, here is a sample TypeScript:
generates the following JavaScript:
let's focus on the line Tristate[Tristate["False"] = 0] = "False";
. Within it Tristate["False"] = 0
should be self explanatory, i.e. sets "False"
member of Tristate
variable to be 0
. Note that in JavaScript the assignment operator returns the assigned value (in this case 0
). Therefore the next thing executed by the JavaScript runtime is Tristate[0] = "False"
. This means that you can use the Tristate
variable to convert a string version of the enum to a number or a number version of the enum to a string. This is demonstrated below:
Changing the number associated with a Number Enum
By default enums are 0
based and then each subsequent value increments by 1 automatically. As an example consider the following:
However, you can change the number associated with any enum member by assigning to it specifically. This is demonstrated below where we start at 3 and start incrementing from there:
TIP: I quite commonly initialize the first enum with
= 1
as it allows me to do a safe truthy check on an enum value.
Number Enums as flags
One excellent use of enums is the ability to use enums as Flags
. Flags allow you to check if a certain condition from a set of conditions is true. Consider the following example where we have a set of properties about animals:
Here we are using the left shift operator to move 1
around a certain level of bits to come up with bitwise disjoint numbers 0001
, 0010
, 0100
and 1000
(these are decimals 1
,2
,4
,8
if you are curious). The bitwise operators |
(or) / &
(and) / ~
(not) are your best friends when working with flags and are demonstrated below:
Here:
we used
|=
to add flagsa combination of
&=
and~
to clear a flag|
to combine flags
Note: you can combine flags to create convenient shortcuts within the enum definition e.g.
EndangeredFlyingClawedFishEating
below:
String Enums
We've only looked at enums where the member values are number
s. You are actually allowed to have enum members with string values as well. e.g.
These can be easier to deal with and debug as they provide meaningful / debuggable string values.
You can use these values to do simple string comparisons. e.g.
Const Enums
If you have an enum definition like the following:
The line var lie = Tristate.False
is compiled to the JavaScript var lie = Tristate.False
(yes, output is same as input). This means that at execution the runtime will need to lookup Tristate
and then Tristate.False
. To get a performance boost here you can mark the enum
as a const enum
. This is demonstrated below:
generates the JavaScript:
i.e. the compiler: 1. Inlines any usages of the enum (0
instead of Tristate.False
). 1. Does not generate any JavaScript for the enum definition (there is no Tristate
variable at runtime) as its usages are inlined.
Const enum preserveConstEnums
Inlining has obvious performance benefits. The fact that there is no Tristate
variable at runtime is simply the compiler helping you out by not generating JavaScript that is not actually used at runtime. However, you might want the compiler to still generate the JavaScript version of the enum definition for stuff like number to string or string to number lookups as we saw. In this case you can use the compiler flag --preserveConstEnums
and it will still generate the var Tristate
definition so that you can use Tristate["False"]
or Tristate[0]
manually at runtime if you want. This does not impact inlining in any way.
Enum with static functions
You can use the declaration enum
+ namespace
merging to add static methods to an enum. The following demonstrates an example where we add a static member isBusinessDay
to an enum Weekday
:
Enums are open ended
NOTE: open ended enums are only relevant if you are not using modules. You should be using modules. Hence this section is last.
Here is the generated JavaScript for an enum shown again:
We already explained the Tristate[Tristate["False"] = 0] = "False";
portion. Now notice the surrounding code (function (Tristate) { /*code here */ })(Tristate || (Tristate = {}));
specifically the (Tristate || (Tristate = {}));
portion. This basically captures a local variable TriState
that will either point to an already defined Tristate
value or initialize it with a new empty {}
object.
This means that you can split (and extend) an enum definition across multiple files. For example below we have split the definition for Color
into two blocks
Note that you should reinitialize the first member (here DarkRed = 3
) in a continuation of an enum to get the generated code not clobber values from a previous definition (i.e. the 0
, 1
, ... so on values). TypeScript will warn you if you don't anyways (error message In an enum with multiple declarations, only one declaration can omit an initializer for its first enum element.
).
Last updated