Introduction

Sometimes rigid structure can allow us to build robust and solid foundations for modern applications, and TypeScript’s Enums will enable us to do precisely that.

Since version 2.4 of TypeScript, we have been able to use Enums, a data type provided to us with configurability and “flexible rigidity” in mind.

What are Enums?

To be more specific, Enums are a data type that allows us to create a set of constant Key:Value pairs, enabling us to document intent much more quickly.

Enums come under three distinct categories or three different types:

  • Numeric
  • String
  • Heterogeneous

Each of these types comes with its use cases and particularities which we’ll discuss in this article.

Numeric Enums

The Numeric Enums are sets of Key:Value pairs where a number represents the value of each key.

One example of a use case would be storing the keyCode of various keyboard keys as a value under the key’s name key in our Enum.

The keyCode property of an event in JavaScript has been deprecated, but we’ll only use that as an example of a real-life scenario for using Numeric Enums.

enum KeyboardKeysCodesEnum {
  KeyA = 65,
  KeyB = 66,
  KeyC = 67,
  ...
  KeyZ = 90,
}

Additionally, one cool feature is that Numeric Enums are self-incrementing, meaning that if we were to define empty keys, the values for the keys would start from zero and increment by one for each following key.

For example:

enum ValuesEnum {
  ZERO,
  ONE,
  TWO,
  THREE
}

If we were to log the value for each of those keys, we would see that they would, indeed, be precisely what their key’s name is:

ValuesEnum.ZERO -> 0
ValuesEnum.ONE -> 1
ValuesEnum.TWO -> 2
ValuesEnum.THREE -> 3

String Enums

The second type of an enum would be that of String Enums, where a value of type “string” would represent the value of each key within the set.

They’re handy when defining or constraining specific actions that the user can do; for example, we might choose to show a handful of hardcoded links to a user depending on their privileges:

enum AdminLinks = {
  CREATE_NEW_POST = <create-new-post-link>,
  DASHBOARD = <dashboard-link>,
  ...
}

enum UserLinks = {
  ABOUT_US = <about-us-link>,
  POSTS = <posts-link>,
  ...
}

You will also find String Enums especially useful when defining Redux Actions or Configuration Objects:

export enum UploadedImagesActions {
  UPLOAD_IMAGE = 'UPLOAD_IMAGE',
  REMOVE_IMAGE = 'REMOVE_IMAGE',
}
export enum SidebarOptionsTitles {
  PICTURES = 'pictures',
  LAYOUTS = 'layouts',
}

export enum SidebarOptionsDescriptions {
  PICTURES = 'Drag an image onto the page in order to add it.',
  LAYOUTS = 'Select a layout in order to apply it',
}

Heterogeneous Enums

Having talked about Numeric & String Enums, we’ve now reached the Heterogeneous ones, which are simply a combination of the first two, in the sense that the values of the set can be, within the same set, both string and number:

enum HeterogeneousEnum {
  ONE = 1,
  TWO = "2",
  THREE = 3,
  FOUR = "4"
}

Computed & Constant Members

A member refers to the Key:Value pair within an Enum.

The value of each member within an Enum might be a constant or a computed one.

Constant Members

constant member is either:

  • The first one in the Enum has no initializer (Will be assigned the value of 0)
  • A member that precedes a constant numeric member, in which case its value is going to be that of the previous member incremented by 1
  • The enum member is initialized with a constant enum expression. A constant enum expression is a subset of TypeScript expressions that can be thoroughly evaluated at compile time.

Constant Enum Expression

An expression is a constant enum expression if it is:

  • A literal Enum expression (Which is a string literal or a numeric literal)
  • A reference to a previously defined constant Enum member (Which can originate from a different enum)
  • parenthesized constant enum expression
  • One of the +, -, ~ unary operators applied to constant Enum expression
  • One of the +, -, *, /, %, <<, >>, >>>, &, |, or ^ binary operators with constant Enum expressions as operands.
enum SingleMemberEnum {
  VALUE,
}
enum MultiMemberEnum {
  FirstValue = 5,
  SecondValue,
  ThirdValue
}

Computed Members

In all cases, apart from the ones mentioned in the previous sections, members will be classified as Computed Members.

enum FileAccess {
  // Constant Members
  None,
  Read = 1 << 1,
  Write = 1 << 2,
  ReadWrite = Read | Write,
  // Computed Member
  G = "123".length,
}

Reverse Mapping

When we interact with TypeScript’s Enums, we have access not only to the members’ values but also to the members’ names as well:

If we were to make use of an Enum that we’ve declared above, HeterogeneousEnum:

enum HeterogeneousEnum {
  ONE = 1,
  TWO = "2",
  THREE = 3,
  FOUR = "4"
}

To access the value of, let’s say, the 2nd member, we would do so as such:

console.log(HeterogeneousEnum.TWO) // “2”

However, if we were to need to access the name of the member, we would have to use the square bracket notation with the index of the member within the Enum:

console.log(HeterogeneousEnum[1]) // “TWO”

Iterating Over An Enum

Let’s say that we wanted to render multiple cards based on the members of an Enum; we already know how to retrieve a specific name or value of a member, but how do we iterate over them?

Well, it certainly is possible, and if we were to look at one of the examples shown earlier, we should notice that that’s what it’s been done with this Enum:

export enum SidebarOptionsTitles {
  PICTURES = 'pictures',
  LAYOUTS = 'layouts',
}

export enum SidebarOptionsDescriptions {
  PICTURES = 'Drag an image onto the page in order to add it.',
  LAYOUTS = 'Select a layout in order to apply it',
}

The set has been used precisely to hardcode sidebar panels, depending on what we store in the Enum.

The Redux code where that’s been used looks something like this:

const mappedOptions = Object.values(OPTIONS);

{mappedOptions.map((option) => {
  const processedOptionName = `${option.charAt(0).toUpperCase()}${option.slice(1)}`;
  const elementClass = activeOption === option ? styles.active : undefined;
  
  return (
    <li
      key={option}
      className={elementClass}
      onClick={() => changeActiveOption(option)}
    >
      {processedOptionName}
    </li>
  );
})}

As you may see, all we’ve done was extract the values of an “objectified” Enum the same way we would with a JavaScript object.

If you’d like to learn how to add TypeScript to your React project, check out this article where we do exactly that!

Summary

So, that was it. We’ve been able to go over the following successfully:

  • What an Enum is
  • What are the different types of Enums
    • Numeric
    • String
    • Heterogenous
  • Constant Members
  • Computed Members
  • Reverse Mapping
  • Iterating Over An Enum

I hope you’ve enjoyed the read and have gotten a better understanding of the concept at hand.

Feel free to leave any feedback through a comment using the Comments Section below.

Cheers!

👋 Hey, I'm Vlad Mihet
I'm Vlad Mihet, a blogger & Full-Stack Engineer who loves teaching others and helping small businesses develop and improve their technical solutions & digital presence.

💬 Leave a comment

Your email address will not be published. Required fields are marked *

We will never share your email with anyone else.