ncjamieson

TypeScript: Don’t Export const enums

December 14, 2019 • 2 minute read

Brick wall
Photo by Waldemar Brandt on Unsplash

If you are writing a library and you export a const enum, some developers will not be able to compile their applications if they import your library. Let’s look at why.

Non-const enums

When you declare an enum, TypeScript will generate code for it. For example, this TypeScript snippet:

enum Bool {
  True,
  False,
  FileNotFound
}
let value = Bool.FileNotFound;

will compile to this JavaScript:

var Bool;
(function(Bool) {
  Bool[(Bool["True"] = 0)] = "True";
  Bool[(Bool["False"] = 1)] = "False";
  Bool[(Bool["FileNotFound"] = 2)] = "FileNotFound";
})(Bool || (Bool = {}));
let value = Bool.FileNotFound;

The reasons for this are explained in the documentation. However, some developers don’t need the features provided by this style of declaration and don’t want the costs involved — they just want to use enums instead of constants.

const enums

When an enum is declared as const, TypeScript doesn’t generate code for the declaration. For example, this TypeScript snippet:

const enum Bool {
  True,
  False,
  FileNotFound
}
let value = Bool.FileNotFound;

will compile to this JavaScript:

let value = 2 /* FileNotFound */;

No code is generated for the enum declaration. Which is great — it’s just like using a constant — but there is a problem.

Isolated modules

In the above snippets, TypeScript has access to the const enum declaration, as it’s in the same module as the declaration for the value variable.

However, if the const enum declaration is in a different module — and is imported into the module that contains the variable declaration — TypeScript will have to read both modules to determine that Bool.FileNotFound should be replaced with 2.

This is a problem because some developers use a workflow that separates type checking from compilation — with compilation happening on an isolated-module basis:

  • the compiler reads a TypeScript module;
  • the module’s type information is stripped; and
  • what’s left is the JavaScript module that the compiler writes.

This compilation process does not read imported modules, so it’s not possible for it to support the replacement of const enum members — like Bool.FileNotFound — with their values.

The transpileModule function in the TypeScript compiler API performs this type of compilation, as does @babel/plugin-transform-typescript — which is what’s used in create-react-app.

TypeScript has an isolatedModules compiler option that performs additional checks to ensure that the compiled code is safe for this type of compilation process. If you are are writing a library, you should enable this option to ensure that you are not exporting const enum declarations and that all TypeScript developers can compile code that imports your library.


© 2020 Nicholas Jamieson All Rights Reserved