Using TypeScript, comparably to other programming languages, we can define finite numerical (and string-based) sets by using a data structure called enumeration. As I am going to prove later in this article, enumerations fit well together with other TypeScript constructs like namespaces and literals.

Enumeration with numbers

We can create an enumeration type indexed with a number value:

I applied the const modifier to the aforementioned snippet to declare ENumbers — this ensures that the structure leaves no additional JavaScript code during runtime (if for some reasons we want the TypeScript compiler to emit an object with the defined properties, we can leave it without const). Interestingly enough, we are allowed to merge multiple const enum statements across our codebase but obviously cannot merge const enum and regular enum statements for the same type — this stems from the fact that at its core, enum declarations behave in a similar fashion to interface declarations.

We can declare a similar enumeration concept using number literals:

Using number literals makes such definition more terse at the expense of losing all the previously defined labels. For what is worth, types never leave any runtime trace as well.

Enumeration with strings

Similarly to numbers, we can define enumeration types using strings, like in the following snippet:

As you might realise, the definition seems to contain complexity which is not required (the label and the value is the same). Another question is whether we should use upper case letters for labels or not.

ELetters can be reduced to a string literal with ease:

Interoperability of enumerations and namespaces

We can use the name of the enumeration type to define an namespace. We can use such a namespace to e.g. allow a function to belong to a certain type:

In the aforedefined example, we defined a function called getPrice that can be called in a special fashion: ECountry.getPrice(...) . Naturally, we can leverage this pattern in multitude of different ways, defining other structures — please note though that overusing it might result in a rather unmaintainable codebase.

Interoperability of enumerations and literals

As our enumeration types might hold a lot of additional code which is not related to the very enumeration aspect of them, we might want to abstract them away (or when we want to distribute a codebase as a library and we don’t want to expose enumeration types). This is possible by defining a type literal which values correspond to the values of the enumeration.

Let’s define a type literal for the ECountry enumeration and let’s define an interface that returns pricing methods:

Please note that the IPricing.getPrice function requires a TCountry type, not ECountry. Thanks to the structural typing properties of the TypeScript language, we can substitute TCountry with ECountry nevertheless:

This discovery allows us to treat enums as potential vectors for different business logics. One obvious application could be having different enums for production and for testing code. Whether this pattern is viable for a business project or not, I will leave that to the reader to decide.

Summary

As we might have seen, TypeScript allows us to convey enumeration using either enumeration types or literal types and it is possible to use both approached interchangeably. Personably, I prefer using literal types as the structure is way shorter and I get the same type support from the TypeScript compiler as compared to using enumeration types.

Full-stack Software Developer that loves building products.