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 enum
s 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";
A 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 enum
s also prevent you from doing some of the stranger things that are are allowed with standard enum
s, such as:
//TypeScript:
Color[5] === "Cyan"; //true
Color[5] = "Polkadot";
Color[5] === "Cyan"; //false
Since you can't index by number in const enum
s, 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!