Matej
Space of Matej

Space of Matej

The difference between enum, const enum and declare enum in Typescript

The difference between enum, const enum and declare enum in Typescript

Learn the correct usage of the different types of enums in Typescript!

Matej's photo
Matej

Published on Sep 15, 2021

4 min read

While working on a Typescript project, I always wondered what the difference between enum, const enum and declare enum is, but I never looked it up. I always thought the difference is in syntactic sugar, and that it eventually becomes the same thing when it gets interpreted.

Let's dig into it:

enum

enum DownloadStatus {
    Idle = "idle",
    Loading = 'loading',
    Done = 'done'
}

let currentStatus: DownloadStatus = DownloadStatus.Idle;
console.log(currentStatus);

This seems pretty reasonable; define DownloadStatus as an enum and set it to the currentStatus variable.

In Javascript, it looks like this:

"use strict";
var DownloadStatus;
(function (DownloadStatus) {
    DownloadStatus["Idle"] = "idle";
    DownloadStatus["Loading"] = "loading";
    DownloadStatus["Done"] = "done";
})(DownloadStatus || (DownloadStatus = {}));
const currentStatus = DownloadStatus.Idle;
console.log(currentStatus);

The product is a self-invoking function, which sets the DownloadStatus to an object. The keys are enum member names, and values are the enum member values.

Then it sets the currentStatus to the value of the Idle member and logs it.

const enum

Let's change our DownloadStatus into a const enum:

const enum DownloadStatus {
    Idle = "idle",
    Loading = 'loading',
    Done = 'done'
}

const currentStatus: DownloadStatus = DownloadStatus.Idle;

console.log(currentStatus);

Then the compiler spits out this:

"use strict";
const currentStatus = "idle" /* Idle */;
console.log(currentStatus);

Woah, so it just skipped the whole DownloadStatus part and set the value to idle then logged it.

This is something called substitution or inlining. What it does is it takes only the value of our enum member and sets it directly, without making it an object. This is especially useful for frontend applications since we always want to decrease the bundle size as much as possible.

So it would make sense always to use them, right? Well, not really.

Let's say we want to export our enum and use it later in an onClick event, for example:

# DownloadStatus.ts
export const enum DownloadStatus {
    Idle = "idle",
    Loading = 'loading',
    Done = 'done'
}

and an onClick handler:

#eventHandlers.ts
import { DownloadStatus } from 'DownloadStatus';

const handleOnClick = () => {
    const status = DownloadStatus.Loading;
}

const element = document.getElementById(id);
element.addEventListened('click', handleOnClick)

The output in our console becomes this: error TS2476: A const enum member can only be accessed using a string literal.

We get this because the const enum is actually not existing as a value anymore. In the file, it got inlined, so wherever it was, it appears as just a string literal, but if we want to use it post-compilation it becomes an issue since,e well, it doesn't exist.

Ok we have covered const enums and enums. What about declare enum?

declare enum

Okay, let's set our DownloadSpeed to become a declare enum:

declare enum DownloadStatus {
    Idle = "idle",
    Loading = 'loading',
    Done = 'done'
}

const currentStatus: DownloadStatus = DownloadStatus.Idle;

console.log(currentStatus);

The Javascript part looks like this:

"use strict";
const currentStatus = DownloadStatus.Idle;
console.log(currentStatus);

It looks almost the same as the const enum example, but with a slight difference, it's not set to a string literal of "idle" but actually to DownloadStatus.Idle. Hmm, what if we try to run it?

[ERR]: DownloadStatus is not defined

Huh? Weird, but we did declare it?

Well, we declared it, but it's not defined anywhere! What the declare solves is a different issue. For example, if we're using the jQuery library that is imported as a script rather than npm, what happens is that Typescript doesn't know about it, so what we would need to do is use declare const $: any, which would tell typescript that "this actually exists on a global level, so here's how it works."

With that, you can actually use jQuery or any other library as intended without typescript compiler errors.

Conclusion

So as we can see, there's actually quite a difference in those enums! I hope it brought some clarity on the topic of this powerful concept.

I'm curious what are your usages of enums, and how do you apply them?

I hope you learned something from this post. Thanks for reading! 👋

 
Share this