Inlining constants with const enums in TypeScript
An often overlooked part of writing high performance JavaScript apps is to consider how your code will be compiled and minified.
Let’s look at some TypeScript and what comes out of tsc
when compiled (playground):
// TypeScript
const first = 1;
const second = 2;
console.log(first + second);
// Compiled
"use strict";
const first = 1;
const second = 2;
console.log(first + second);
The exact output depends on some of the compiler options, but it’s probably what you expected it to look like. The problem here is that the constants not only end up taking space in the compiled bundle, but also need to get parsed/run by the JavaScript engine when the code is run.
One of my favourite tricks in TypeScript that I’ve started doing recently is inlining these constants using const enum
s. If you weren’t aware, regular enum
s generate a bunch of output to allow accessing both the names and the values of the enums at runtime (playground):
// TypeScript
enum Constants {
First = 1,
Second = 2
}
console.log(Constants.First + Constants.Second);
// Compiled
"use strict";
var Constants;
(function (Constants) {
Constants[Constants["First"] = 1] = "First";
Constants[Constants["Second"] = 2] = "Second";
})(Constants || (Constants = {}));
console.log(Constants.First + Constants.Second);
But, this is not the case for const enum
s (playground):
// TypeScript
const enum Constants {
First = 1,
Second = 2
}
console.log(Constants.First + Constants.Second);
// Compiled
"use strict";
console.log(1 /* First */ + 2 /* Second */);
That’s much smaller! When this is run through a minifier it will probably end up as the minimal console.log(1+2)
or console.log(3)
code for this example, which is less code to download, parse and run.
Something to keep in mind when applying this technique is that if the constant ends up being used several times it may end up having the opposite effect, especially if the constant value is lengthy (playground):
// TypeScript
const enum Constants {
Long = 'a very long string'
}
console.log(Constants.Long);
console.log(Constants.Long);
console.log(Constants.Long);
// Compiled
"use strict";
console.log("a very long string" /* Long */);
console.log("a very long string" /* Long */);
console.log("a very long string" /* Long */);
Compared to a regular constant (playground):
// TypeScript
const long = 'a very long string';
console.log(long);
console.log(long);
console.log(long);
// Compiled
"use strict";
const long = 'a very long string';
console.log(long);
console.log(long);
console.log(long);
It’s also worth mentioning that const enum
s are not the be-all and end-all of enums in TypeScript, the main reason you would want to opt for a regular enum
is if you’re developing a library and exporting an enum as part of its API as the enum members need to be available at runtime.