Enums in TypeScript
Exploring TypeScript
Enumerations are user-defined types consisting of named values (members) that behave as constants. They’re useful when you want to define a type consisting of a restricted set of values, for example the colours of the rainbow.
Numeric enums
By default, numeric enums start at zero and increment by 1 for each subsequent member:
enum Volume {
Low,
Medium,
High
}
console.log(Volume.Low); // 0
console.log(Volume.Medium); // 1
console.log(Volume.High); // 2
You can change this if you wish and either start with a non-zero value or explictly assign values to members:
enum StoryPoints {
Zero = 0,
One = 1,
Two = 2,
Three = 3,
Five = 5,
Eight = 8,
Thirteen = 13
}
console.log(StoryPoints.Eight); // 8
Numeric enums can be reverse mapped, in other words you can use the numeric value to retrieve the associated member:
console.log(StoryPoints[3]); // Three
String enums
TypeScript version 2.4 introduced support for string initialisers in enums. This can be useful for defining a restricted set of string values:
enum MimeTypes {
CSV = "text/csv",
HTML = "text/html",
JSON = "application/json",
XML = "application/xml"
}
console.log(MimeTypes.JSON); // application/json
As the release notes state, you cannot reverse map a string-based enum:
console.log(MimeTypes["application/xml"]); // Error!
Bitwise enums
Bitwise enums (also known as Flag enums) are useful when you want flexbility in combining enum members to represent new values. One such use might be where you want to elevate or remove file access permissions:
enum FileAccess {
None = 0,
Read = 1 << 0, // Can also be expressed as Read = 1
Write = 1 << 1, // Can also be expressed as Write = 2
Delete = 1 << 2 // Can also be expressed as Delete = 4
}
While the FileAccess
enum could be defined with explicit members for every combination, as that number increases the approach quickly becomes impractical.
As you may have noticed from the above example, for bitwise enums use values that are powers of 2 (2^0, 2^1, 2^2, 2^3, and so on).
And in action:
let access = FileAccess.None;
console.log(access); // 0
// Elevate to Read access
access = FileAccess.Read;
console.log(access); // 1
// Elevate to Read and Write access
access |= FileAccess.Write;
console.log(access); // 3
// Elevate to Read, Write, and Delete access
access |= FileAccess.Delete;
console.log(access); // 7
// Remove Delete access
access &= ~FileAccess.Delete;
console.log(access); // 3
// Evaluating incorrectly
console.log(access === FileAccess.Read); // false
// Evaluating correctly
console.log(FileAccess.Read === (access & FileAccess.Read)); // true
Note the |=
and &= ~
operators. The |=
operator allows to perserve any existing assigned value(s) - using =
alone will overwrite. The &= ~
operator allows you to remove one or more assigned values.
const
enums
If you wish to avoid the overhead of the generated code and additional indirection, you can use a const
enum:
const enum Rainbow { // Note the const modifier
...
}
With a const
enum, the values are inlined with each use. Note that only constant enum expressions can be used, you cannot use computed values as in this contrived example:
const enum StoryPoints {
Zero = 0,
One = 1,
Two = 2,
Three = One | Two,
Five = ".....".length() // Error!
Eight = 8,
Thirteen = 13
}