How to create enums in JavaScript

Apr 29, 2023#javascript#how-to

Enums, short for “enumerations”, are a programming language feature that allows developers to define a set of named constants. These constants represent a fixed set of values that a variable can take on. Enums can be used to make code more readable and self-documenting by replacing magic numbers or strings with meaningful names.

Enums are particularly useful when you have a set of related values that you want to group together, such as days of the week, colors, or error codes. By defining an enum, you can ensure that only valid values are used in your code and catch errors at compile time instead of at runtime.

For example, you could define an enum for color with meaningful names such as RED, GREEN, and BLUE. This makes the code more readable and easier to understand.

function draw(color) {
  switch (color) {
    case Color.RED:
      console.log('draw something red')
      break;
    case Color.GREEN:
      console.log('draw something green')
      break;
    case Color.BLUE:
      console.log('draw something blue')
      break;
    default:
      console.log('invalid color')
  }
}

draw(Color.GREEN) // draw something green

In JavaScript, there is no built-in support for enums like in other programming languages, but there are a few ways to emulate enums.

  1. Using object literals. A simple and straightforward way to define constants with meaningful names.
const Color = {
  RED: 'red',
  GREEN: 'green',
  BLUE: 'blue'
}
  1. Using function-based enums. This approach allows you to define more complex enums with additional properties and behavior.
function Enum(values) {
  for (let i = 0; i < values.length; i++) {
    this[values[i]] = i;
  }
  this.names = function() {
    return Object.keys(this).filter(key => typeof this[key] !== 'function');
  }
}

const Color = new Enum(['RED', 'GREEN', 'BLUE']);
  1. Using Symbol-based enums. Symbols are unique and immutable values that can be used as object properties.
const Color = {
  RED: Symbol('red'),
  GREEN: Symbol('green'),
  BLUE: Symbol('blue')
}
  1. Using class-based enums. You can use the class static properties as enum constants in your code. You can also add methods to the class to provide additional behavior or properties to your enum values.
class Color {
  static RED = new Color('red');
  static GREEN = new Color('green');
  static BLUE = new Color('blue');
  
  constructor(name) {
    this.name = name;
  }
  
  toString() {
    return this.name;
  }
}
  1. Using closure-based enums. Define an enum using a closure that returns an object with getters for each enum value.
const Color = (function() {
  const values = {
    RED: 'red',
    GREEN: 'green',
    BLUE: 'blue'
  };
  
  const ColorEnum = {};
  
  Object.keys(values).forEach(key => {
    Object.defineProperty(ColorEnum, key, {
      get: function() {
        return values[key];
      }
    });
  });
  
  return ColorEnum;
})();
  1. Using proxy-based enums. A way to define an enum using a JavaScript proxy object that intercepts property access and returns a constant value.
const Color = new Proxy({}, {
  get: function(target, name) {
    if (name === 'toJSON') {
      return undefined;
    }
    return name;
  }
});

Note that closure-based and proxy-based enums are less common and may not be as widely used as the other approaches mentioned earlier. However, they are still viable options depending on your specific use case.

Using TypeScript

[TypeScript has native support for enums], which makes it easy to define and use enums in your code. TypeScript enums can be used as types, which means that you can define variables, functions, and parameters with an enum type. This helps to ensure that only valid enum values are used in your code, and can catch errors at compile time instead of at runtime.

  • Numeric enums
enum Direction {
  Up,
  Down,
  Left,
  Right,
}

When this code is compiled to JavaScript, it produces the following:

"use strict";
var Direction;
(function (Direction) {
    Direction[Direction["Up"] = 0] = "Up";
    Direction[Direction["Down"] = 1] = "Down";
    Direction[Direction["Left"] = 2] = "Left";
    Direction[Direction["Right"] = 3] = "Right";
})(Direction || (Direction = {}));
  • String enums
enum Color {
  Red = "red",
  Green = "green",
  Blue = "blue",
}

When this code is compiled to JavaScript, it produces the following:

"use strict";
var Color;
(function (Color) {
    Color["Red"] = "red";
    Color["Green"] = "green";
    Color["Blue"] = "blue";
})(Color || (Color = {}));

In both cases, TypeScript enums are compiled to JavaScript objects with properties for each enum member. The members are defined using either numeric or string values, and the enum object is augmented with reverse mappings from values to names.

Overall, TypeScript enums provide a more robust and reliable way to define enums in your code than emulating them in JavaScript.