TypeScript: Supercharging enums with modules.

George Stefanis
George Stefanis

When using a language like Java you can use enums to do some very interesting things. For example a Java enum can look like so:

public enum Directions{
  EAST ("E"), 
  WEST ("W"), 
  NORTH ("N"), 
  SOUTH ("S")
  ; 
  /* Important Note: Must have semicolon at
   * the end when there is a enum field or method
   */
  private final String shortCode;
	  
  Directions(String code) {
      this.shortCode = code;
  }
	  
  public String getDirectionCode() {
      return this.shortCode;
  }
}

There are a lot of nice things here to extract.

  • The Enum variables themselves
  • The private fields/methods
  • The functions

Typescript Enum

I wanted to emulate that in Typescript. The easiest way I found was to use an Enum along with a Module.

enum Direction {
  East = 'E',
  West = 'W',
  North = 'N',
  South = 'S',
};

module Direction {
  let shortCode: string = '';

  export function setShortCode(code:string) {
    shortCode = code;
  }

  export function getDirectionCode() : string {
    return shortCode
  }
}

In essence this achieves almost the same thing. Yes, you miss the constructor. Also it doesn't have a direct access to the enum but that's something you can pass in as a parameter to the function.

One caviat though. ESLint, depending on your setup, will complain about it. The reason being that your enum and the module have the same name. You can either be a good citizen and rename one or ignore the linter with a few different ways.

Why does that work in JavaScript?

An interesting question. If the names are the same, won't that be a problem? To understand why this works we can have a quick trip to TypeScript Playground and observe what TypeScript transpiles to in Javascript.

"use strict";
var Direction;
(function (Direction) {
    Direction["East"] = "E";
    Direction["West"] = "W";
    Direction["North"] = "N";
    Direction["South"] = "S";
})(Direction || (Direction = {}));
;
(function (Direction) {
    let shortCode = '';
    function setShortCode(code) {
        shortCode = code;
    }
    Direction.setShortCode = setShortCode;
    function getDirectionCode() {
        return shortCode;
    }
    Direction.getDirectionCode = getDirectionCode;
})(Direction || (Direction = {}));

You can see that both the enum code and the module code is isolated by the rest of the application by being wrapped with parenthesis and then invoked immediately. This pattern is almost as old as JavaScript itself and it's called Encaptulation with anonymous functions. So the fact that you named them the same in the end of day doesn't do any harm. I'd go as far to say it doesn't even matter.