Enums in TypeScript
Tags: typescript
Published: 2020-02-28 00:00:00

---
title: Enums in TypeScript
date: "2020-02-28T00:00:00.000Z"
tags: ["typescript"]
---

[Enumerations](https://en.wikipedia.org/wiki/Enumerated_type) 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:

~~~typescript
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:

~~~typescript
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](https://www.typescriptlang.org/docs/handbook/enums.html#reverse-mappings), in other words you can use the numeric value to retrieve the associated member:

~~~typescript
console.log(StoryPoints[3]); // Three
~~~

---

## String enums
[TypeScript version 2.4](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-4.html#string-enums) introduced support for string initialisers in enums. This can be useful for defining a restricted set of string values:

~~~typescript
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:

~~~typescript
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:

~~~typescript
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<sup>^</sup>0, 2<sup>^</sup>1, 2<sup>^</sup>2, 2<sup>^</sup>3, and so on).

And in action:

~~~typescript
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:

~~~typescript
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:

~~~typescript
const enum StoryPoints {
	Zero = 0,
	One = 1,
	Two = 2,
	Three = One | Two,
	Five = ".....".length() // Error!
	Eight = 8,
	Thirteen = 13
}
~~~