Blog Article:

const enums in TypeScript 1.4 and how they differ from standard enums

by Joel Marshall on March 05, 2015

TypeScript 1.4 saw the introduction of the const enum type. Enumerations have been around since version 0.9, but we're just seeing constant enumerations for the first time. So how do they differ? Well, maybe not in the way(s) that you expected, so let's take a look.

First, let's create an enum without the const modifier and then see what it looks like once the TypeScript compiler turns it into javascript:

That makes sense, right? Now let's look at what happens when we introduce the const modifier:

No, that's not a mistake- that's just all we get from the compiler. In many cases, this may be all you need. In the case of const enums we're only getting design/compile time checking against the type, as , by default, the compiler doesn't generate an object for us as it does with a standard enum (this behavior can be changed with the --preserveConstEnums flag).

So what does that mean in practice? Well, we can still add to both enumeration types with multiple declarations, as long as we include an initializer on subsequent declarations:

//TypeScript:
enum Color {Red, Green, Blue};
const enum ConstantColor {Red, Green, Blue};

enum Color {Yellow = 3, Magenta, Cyan}; //This compiles fine since we initialized the first element
const enum ConstantColor{Yellow = 3, Magenta, Cyan}; //So does this

//Compiled javascript:
var Color;
(function (Color) {
    Color[Color["Red"] = 0] = "Red";
    Color[Color["Green"] = 1] = "Green";
    Color[Color["Blue"] = 2] = "Blue";
})(Color || (Color = {}));
;
;
var Color;
(function (Color) {
    Color[Color["Yellow"] = 3] = "Yellow";
    Color[Color["Magenta"] = 4] = "Magenta";
    Color[Color["Cyan"] = 5] = "Cyan";
})(Color || (Color = {}));
;
;

And we can get the indices of both types' elements through dot notation:

//TypeScript:
var mycolor = Color.Red;
var myconstcolor = ConstantColor.Red;

//Compiled javascript:
var mycolor = 0 /* Red */;
var myconstcolor = 0 /* Red */;
 

The differences start to show up when we start indexing into the enumerations as arrays. We can still index by string literal like so:

//TypeScript:
var blue = ConstantColor["Blue"]; 
var constantBlue = ConstantColor["Blue"];

//Compiled javascript
var blue = 2 /* "Blue" */;
var constantBlue = 2 /* "Blue" */;

But whereas a standard enum will let us index by number:

//TypeScript:
var cyanString = Color[5];
cyanString === "Cyan"; //true

//Compiled javascript
var cyanString = Color[5];
cyanString === "Cyan";

const enum will won't compile if we try to do this:

//TypeScript:
var constCyanString = ConstantColor[5]; //Error

//Won't compile.
//Index expression argument of 'const' enum must be a string literal.

And this is where the const part comes in. The const enum type doesn't care about the string name of each element in the enumeration. As we've seen, the const enum doesn't even compile into an object- for the default behavior of the compiler it's purely a design/compile time construct.

According to the TypeScript 1.4 release announcement:

"For heavy uses of enums, it’s helpful to have an even more restricted form that we know is safe to always inline. This helps with performance, code size, and with working with cases where the enum aliases themselves may not be exported"

const enums also prevent you from doing some of the stranger things that are are allowed with standard enums, such as:

//TypeScript:
Color[5] === "Cyan"; //true
Color[5] = "Polkadot";
Color[5] === "Cyan"; //false

Since you can't index by number in const enums, changing the meaning of elements in the enumeration isn't possible. And while you can add properties to both enum types through string indexing like so:

//TypeScript:
ConstantColor["DoesntExist"] = 50;
Color["DoesntExist"] = 50;

Compiled javascript:
ConstantColor["DoesntExist"] = 50;
Color["DoesntExist"] = 50;

You're really not buying yourself anything as TypeScript won't pick these declarations up as new members of the enumerations. Note that this is the first and only time in these examples that we've seen the ConstantColor object compiled into javascript. This is because the TypeScript compiler does not see this declaration as part of the constant enumeration, and therefore outputs the assignment to raw javascript.

One little oddity that we'll point out before we finish up- and this applies to both enumeration types. You can form valid TypeScript assignments that result in invalid javascript:

//TypeScript:
Color["Red"] = 12345;
ConstantColor["Red"] = 12345;

Compiled javascript:
0 /* "Red" */ = 12345;
0 /* "Red" */ = 12345;
As you can see, these assignments pass happily through the compiler, but will cause a runtime error since you can't assign a number to a number.

If you liked this article, please share it!

Joel Marshall

Founder

Joel has been developing applications with Microsoft ASP.NET and related web technologies for 15 years. He is passionate about software architecture, design patterns, and emerging web technologies.