Typescript-like enums in Golang

I’ve been learning Golang recently, and while Go’s ultra-simple nature is often refreshing it can be a bit limiting too. One Typescript feature that I found myself missing is enums. While there are a lot of people asking about this, there aren’t a lot of Go-beginner friendly explanations of how to do this out there, so I decided to dig in. After a bit of research I’m happy to report that Go can get pretty darn close!

For the uninitiated, a Typescript enum look like this:

enum Direction {
    Up = 0,
    Down   // typescript will automatically increment the remaining items
    Left,  // based on the starting value (0). 
    Right, // so here, Right = 3
}

The above is pretty handy: now when a user of your code imports Direction they get Up, Down, Left, and Right with it for free. Additionally, any instances of direction (e.g. Up) have type information attached, so the compiler knows that they are instances of the Direction type. Better yet, since Direction is now a full-fledged type you can hang methods (e.g. asString() off of it, allowing you to do things like Direction.asString() later.

Getting Go to do the equivalent is a bit tricky. One common half-solution is to use constants with the iota keyword to create separate constants for each of Up, Down, Left, and Right:

const (
	Up    = iota
	Down  // if the first value in a constant block is `iota` 
	Left  // Go will automatically increment the rest for you.
	Right // so here, Right = 3
)

The iota keyword tells Go to automatically increment our constants. iota returns an int on the first call, and increments itself by 1 on every subsequent use inside a single const block, so the above code would set Up to 0, and Down to 1, etc.

This isn’t bad, but at this point we’ve only declared a bunch of integer constants with no way to represent that Up, Down, Left, and Right are all variants of Direction. More distressingly, we can’t attach any new methods to our Direction type since there is no Direction type (yet)!

One way to solve this is to combine iota with a custom redefinition of the int type. The key is to first declare your custom int type, and then to set the members of your const block to use that type instead of int (which is what iota will give you by default):

type Direction int

const (
	Up   Direction = iota
	Down
	Left
	Right
)

This way, Up, Down, Left, and Right are all of type Direction, but their values are still 0, 1, 2, and 3 respectively. If you want to hang methods onto Direction you can now do so by adding them to Direction:

package main

import "fmt"

type Direction int

const (
	Up Direction = iota
	Down
	Left
	Right
)

func (dir Direction) value() int {
	return int(dir)
}

func main() {
	fmt.Printf("Right's value is: %d\n", Right.value())
}

This is getting pretty close to the typescript version’s usability! The only real difference is that when importing the Direction “enum” from another file you’ll need to import the constants (Up, Down, Left, and Right) individually instead of just importing the Direction enum itself the way you would in Typescript.

You’re also free to use any type you’d like for your custom enum type. For example, if you were working with a JSON API that represented Direction.Up as just "up" you could make your Direction type a string instead, with two minor caveats:

  1. You can no longer use iota since iota won’t know how to increment a string.
  2. You’ll need to explicitly add the Direction type to each line, otherwise Go will just treat it as a standard Go string.

Example:

type Direction string

const (
	Up    Direction = "up"
	Down  Direction = "down"
	Left  Direction = "left"
	Right Direction = "right"
)

This simple approach works well for my needs, let me know in the comments if you know of a better way to get Typescript-style enums in Go!