DEV Community

Cover image for TypeScript: How to use Enums
Chris Bongers
Chris Bongers

Posted on • Originally published at daily-dev-tips.com

TypeScript: How to use Enums

Before diving into Enums in TypeScript, let's take a second to look at what they are.

Enums are a common type in most popular programming languages, and it means they are a collection of constants.

Enums are great for defining constants that we often re-use and can't be any other than these set values.

Using Enums in TypeScript, we quickly gain the option to see what assignments are valid for that Enum.

Declaring a Enum in TypeScript

To declare an Enum in TypeScript, we have to define the enum type with a name, much like how we define a interface in TypeScript.

enum Eeveelutions {
  Eevee,
  Vaporeon,    
  Jolteon,
  Flareon,
  Espeon,
  Umbreon,
  Leafeon,
  Glaceon,
  Sylveon
};
Enter fullscreen mode Exit fullscreen mode

We can then use this Enum throughout our code by using the following syntax:

const basicPokemon = Eeveelutions.Eevee;
Enter fullscreen mode Exit fullscreen mode

You might be wondering what this will return, right?

The default value for enums is numeric, so this would return 0 as enums start at zero.

But we can also define a different starting order:

enum Eeveelutions {
  Eevee = 1,
  Vaporeon,
  Jolteon,
  Flareon,
  Espeon,
  Umbreon,
  Leafeon,
  Glaceon,
  Sylveon
};
Enter fullscreen mode Exit fullscreen mode

Note that I only added an index on the first item. Everything else now shifts up from there as it will increment from there.

For instance:

const basicPokemon = Eeveelutions.Eevee;
// 1
const Sylveon = Eeveelutions.Sylveon;
// 9
Enter fullscreen mode Exit fullscreen mode

You can use any custom offset. Let's try it with ten and see what happens.

enum Eeveelutions {
  Eevee = 10,
  Vaporeon,
  Jolteon,
  Flareon,
  Espeon,
  Umbreon,
  Leafeon,
  Glaceon,
  Sylveon
};
Enter fullscreen mode Exit fullscreen mode

Which will result in the following:

const basicPokemon = Eeveelutions.Eevee;
// 10
const Sylveon = Eeveelutions.Sylveon;
// 18
Enter fullscreen mode Exit fullscreen mode

However, you might want to give custom values to these enums in some cases.
We might want to assign the Pokemon's number as the value.

enum Eeveelutions {
  Eevee = 133,
  Vaporeon = 134,
  Jolteon = 135,
  Flareon = 136,
  Espeon = 196,
  Umbreon = 197,
  Leafeon = 470,
  Glaceon = 471,
  Sylveon = 700
};
Enter fullscreen mode Exit fullscreen mode

If we ask for specific Pokemon, we will return their respective Pokedex number.

const basicPokemon = Eeveelutions.Eevee;
// 133
const Sylveon = Eeveelutions.Sylveon;
// 700
Enter fullscreen mode Exit fullscreen mode

Changing the value

Numeric might be the default, but we can also assign other values to the Enum.

We can choose between:

  • Numeric
  • Computed
  • String
  • Heterogeneous

We've seen numeric in action.
Computed I've actually never really had a use-case for, but you can use functions inside the declaration like this:

const customSize = (input:number) => ( input * 10 )
enum Sizes {
  Base = 10,
  Medium = Base * 10,
  Large = Base * 10 * 100,
  Custom = customSize(12)
}
Sizes.Base;
// 10
Sizes.Large;
// 10000
Sizes.Custom;
// 120
Enter fullscreen mode Exit fullscreen mode

It is possible, but I personally never had a good use-case for it.

Then we get to string values, which is a standard option.
We want to have an enum that can be a specific string.

enum RankType {
  Date = 'DATE',
  Popular = 'POPULAR'
}
RankType.Date;
// 'DATE'
RankType.Popular;
// 'POPULAR'
Enter fullscreen mode Exit fullscreen mode

And the last one is heterogeneous, which means a mix of types, and to be honest, I would strongly urge you not to use this.

It would look something like this:

enum RankType {
  Date = 1,
  Popular = 'POPULAR'
}
Enter fullscreen mode Exit fullscreen mode

So what happens to these Enums?

You might wonder how they will look once computed to JavaScript, right?

Let's look at the first example and see what will happen when we compile it to JavaScript.

enum Eeveelutions {
  Eevee = 133,
  Vaporeon = 134,
  Jolteon = 135,
  Flareon = 136,
  Espeon = 196,
  Umbreon = 197,
  Leafeon = 470,
  Glaceon = 471,
  Sylveon = 700
};

const basicPokemon = Eeveelutions.Eevee;
console.log(basicPokemon);
const Sylveon = Eeveelutions.Sylveon;
console.log(Sylveon);
Enter fullscreen mode Exit fullscreen mode

Now when compiling this, we generate the following JavaScript version of this script:

var Eeveelutions;
(function (Eeveelutions) {
    Eeveelutions[Eeveelutions["Eevee"] = 133] = "Eevee";
    Eeveelutions[Eeveelutions["Vaporeon"] = 134] = "Vaporeon";
    Eeveelutions[Eeveelutions["Jolteon"] = 135] = "Jolteon";
    Eeveelutions[Eeveelutions["Flareon"] = 136] = "Flareon";
    Eeveelutions[Eeveelutions["Espeon"] = 196] = "Espeon";
    Eeveelutions[Eeveelutions["Umbreon"] = 197] = "Umbreon";
    Eeveelutions[Eeveelutions["Leafeon"] = 470] = "Leafeon";
    Eeveelutions[Eeveelutions["Glaceon"] = 471] = "Glaceon";
    Eeveelutions[Eeveelutions["Sylveon"] = 700] = "Sylveon";
})(Eeveelutions || (Eeveelutions = {}));
;
var basicPokemon = Eeveelutions.Eevee;
console.log(basicPokemon);
var Sylveon = Eeveelutions.Sylveon;
console.log(Sylveon);
Enter fullscreen mode Exit fullscreen mode

So basically, TypeScript converted it into a function that it can call to get the correct index.

You can make this a bit more optimal by converting your enum into a const.

const enum Eeveelutions {
  Eevee = 133,
  Vaporeon = 134,
  Jolteon = 135,
  Flareon = 136,
  Espeon = 196,
  Umbreon = 197,
  Leafeon = 470,
  Glaceon = 471,
  Sylveon = 700
};

const basicPokemon = Eeveelutions.Eevee;
console.log(basicPokemon);
const Sylveon = Eeveelutions.Sylveon;
console.log(Sylveon);
Enter fullscreen mode Exit fullscreen mode

Now when we compile the TypeScript we get the following output:

var basicPokemon = 133 /* Eevee */;
console.log(basicPokemon);
var Sylveon = 700 /* Sylveon */;
console.log(Sylveon);
Enter fullscreen mode Exit fullscreen mode

The code slimmed down a lot!
I hope you enjoyed the article, let me know if you have any questions.

Thank you for reading, and let's connect!

Thank you for reading my blog. Feel free to subscribe to my email newsletter and connect on Facebook or Twitter

Top comments (10)

Collapse
 
lexlohr profile image
Alex Lohr

Enums are great for internal use, but are cumbersome when they need to be portable. Once you want to receive them from sources without connection to the enum (e.g. from the server), you need to cast or compare them.

I find the pattern of low-maintenance types better: reuse existing defined structures as type schemas, e.g.

// `as const` is required to actually get to a union:
const eeveelutions = ["Vaporeon", "Jolteon", "Flareon", "Espeon", "Umbreon", "Leafeon", "Glaceon", "Sylveon"] as const
// minimal work on the compile-time types:
type Eeveelutions = typeof eeveelutions[number]
// can be used to type check during runtime:
const isEeveelution = (pokemon: any): pokemon is Eeveelution => eeveelutions.includes(pokemon)
Enter fullscreen mode Exit fullscreen mode
Collapse
 
peerreynders profile image
peerreynders

... and the obligatory Objects vs Enums

const EEVEELUTIONS = {
  Eevee: 133,
  Vaporeon: 134,
  Jolteon: 135,
  Flareon: 136,
  Espeon: 196,
  Umbreon: 197,
  Leafeon: 470,
  Glaceon: 471,
  Sylveon: 700,
} as const;

// Resulting in
// type Eeveelutions = 133 | 134 | 135 | 136 | 196 | 197 | 470 | 471 | 700
type Eeveelutions = typeof EEVEELUTIONS[keyof typeof EEVEELUTIONS];

const basicPokemon = EEVEELUTIONS.Eevee;
console.log(basicPokemon);
const Sylveon = EEVEELUTIONS.Sylveon;
console.log(Sylveon);
Enter fullscreen mode Exit fullscreen mode
Collapse
 
dailydevtips1 profile image
Chris Bongers

Fun fact we actually omit enums 99% of the time for objects in our code base.
Love this example you sketched.

Collapse
 
manuartero profile image
Manuel Artero Anguita 🟨 • Edited

Ey there!
side note to your post

We're trying to avoid Enums at our current code-base. We've detected that in 99% of the situations the programmer is just looking to type "It's a string, but not any string, just one among these options"

In order to avoid ts generating extra code and staying closer to "just adding flavor to js", we're replacing:

from:

enum Eeveelutions {
  Vaporeon, Jolteon, Flareon, Espeon, Umbreon, Leafeon, Glaceon, Sylveon
};
Enter fullscreen mode Exit fullscreen mode

to:

type Eeveelutions = 'Vaporeon' | 'Jolteon' | 'Flareon' | 'Espeon' | 'Umbreon' | 'Leafeon' | 'Glaceon' | 'Sylveon';
Enter fullscreen mode Exit fullscreen mode

Which is what - we - were looking for from the beginning

const eevee.evolution = /* hit auto - complete */
Enter fullscreen mode Exit fullscreen mode
Collapse
 
dailydevtips1 profile image
Chris Bongers

Nice addition πŸ’–

Collapse
 
voiedev profile image
Charles Allen • Edited

So basically, TypeScript converted it into a function that it can call to get the correct index.

I think that's actually an IIFE (function that's invoked immediately) that populates an object (in both directions)

Collapse
 
dailydevtips1 profile image
Chris Bongers

Yep you are actually right there Charles πŸ™Œ

Collapse
 
waylonwalker profile image
Waylon Walker

I've only started using enums, they make you feel super cool everytime you find a sue case.

Collapse
 
dailydevtips1 profile image
Chris Bongers

I'm actually most of the time looking how to get rid of them.
They do have particular use-cases, but often they can be replaced with an object lookup.

They hold some superpowers for sure, but I personally try to avoid them most cases πŸ‘€

Collapse
 
guillep2k profile image
Guillermo Prandi

Perhaps function enums are useful for i18n?