Fullstack Developer & Whitewater Kayaker & Scout
When to declare enum and when not and why const enums are not so good
I wrote this post as a reminder to myself that using const enums in a library is a really bad idea.
So in short. If you are writing a library and you export a const enum
then some developers will not
be able to compile their applications if they import your library. Especially when using Babel.
Here we look at why.
When you declare an enum
, Typescript will generate some code for it. This is fine if you are writing
an application code and you do not care what is generated. But when you are writing a library you sometimes
do not need the features provided by this style of declarations - you just want to use enums instead of constants.
This has a solution - you can use const enum
instead of an enum
. Typescript does not generate code for const enum
declaration. This is great - it is just like using constant - but there is a problem.
When you are writing a library you do not have control over your code and how is used or compiled. Nor what compiler
is used and how. Especially Babel and other transpilers operate over one file at a time. That means that the type information
are stripped and a code transformation that depends on understanding the full type system cannot be applied. Simply const enum
imported to another file is gone.
To prevent this error in libraries you should use --isolatedModules
option in your tsconfig.json
file.
Further reading
enum
and const enum
are features from "the old days" of the Typescript where the Javascript landscape was a lot different.
In addition, enums generate a lot of code you probably do not want. And also numeric enums are not type-safe. And instead
of key-value-based enums you can use an object which will serve the same.
const Direction = {
Up: 0,
Down: 1,
Left: 2,
Right: 3,
} as const;
// Get to the const values of any object
type Values<T> = T[keyof T];
// Values<typeof Direction> yields 0 | 1 | 2 | 3
declare function move(direction: Values<typeof Direction>): void;
move(30);
// ^ ๐ฅ This breaks!
move(0);
// ^ ๐ This works!
move(Direction.Left);
// ^ ๐ This also works!
// And now for the Status enum
const Status = {
Admin: 'Admin',
User: 'User',
Moderator: 'Moderator',
} as const;
// Values<typeof Status> yields "Admin" | "User" | "Moderator"
declare function closeThread(threadId: number, status: Values<typeof Status>): void;
closeThread(10, 'Admin'); // All good!
closeThread(10, Status.User); // enum style
A better approach is to use union types. A simple union type gives you something that works similary and is much more aligned with Typescript.
type Status = 'Admin' | 'User' | 'Moderator';
declare function closeThread(threadId: number, status: Status): void;
closeThread(10, 'Admin');
// All good :-)
Further reading
enum
or const enum
in application code--isolatedModules
in tsconfig.json
to true
in librariesconst enum
does not work in libraries