When using the typescript compiler, there is an option to "preserve const enums", which essentially just treats const enums exactly the same as non-const enums. It would be really useful to have the same feature in the babel transform.
export const enum E {
A = 1
}
{
"plugins": [
["transform-typescript", {"preserveConstEnums": true}]
]
}
export let E;
(function (E) {
E[E["A"] = 1] = "A";
})(E || (E = {}));
Throws: 'const' enums are not supported
Add an option that makes const enums behave exactly the same as regular enums.
I want to be able to use babel to transform my typescript in development, as this will be much faster than doing a babel pass and a typescript pass, and babel is needed for other plugins. In production, I can use the slower typescript transform, which handles const enums properly.
Hey @ForbesLindesay! We really appreciate you taking the time to report an issue. The collaborators
on this project attempt to help as many people as possible, but we're a limited number of volunteers,
so it's possible this won't be addressed swiftly.
If you need any help, or just have general Babel or JavaScript questions, we have a vibrant Slack
community that typically always has someone willing to help. You can sign-up here
for an invite.
I'm not sure if this would be a good thing to support... in my opinion, export const enum {...} should actually be a compile error in TypeScript too, as it's impossible to use it correctly with --isolatedModules.
I've always been confused by that flag. Why not just remove const from the enum rather than using a compiler flag?
The reason is that const enums are a useful optimisation in production, and I can handle them by just using the full typescript compiler for the prod build.
In development. I want to make the build as fast as possible by using babel for everything and compiling each module independently.
I use const enum in declaration files(not exporting it), it is useful for distinguishing custom obj type.
The const enum feature of Typescript is really nice. If file size is a concern, it makes sense to use const enums where possible. Just compare the output of the Typescript compiler:

Try it yourself: https://www.typescriptlang.org/play/index.html
For us, loosing the const enum feature would be almost a dealbreaker. We use a lot of enums and if they would be all compiled to the "regular" enum syntax this would add a considerable amount of bytes to our code. Which is not neglectable in our case.
const string enums are even more fantastic, because then you're essentially just dealing with strings.
@andy-ms Do you see any possibility to support this? (Although our specific use case is to generate a unique type that doesn鈥檛 incur any runtime cost, i.e. const enum Foo {} would not emit anything and only be used at compile time, so that might be slightly different.)
EDIT: We went with unique symbols for our use-case.
@nojvek Is there any advantage to a string const enum type over a union of string literal types?
type COUNTRIES = "austria" | "germany" | "greece" | "brazil";
@nicoburns Autocomplete / usability by the developers?
@nicoburns
here's an example where it'd be useful:
const defaultCountry = COUNTRIES.GERMANY
in my own app, I use const enums for my color palette, where it's really nice to avoid hex strings
@mattkrick Can't you also do const defaultCountry = "germany";, which will typecheck just as strictly? Is it about keeping the code size down with an integer rather than a string?
As @mattkrick says, sometimes the values are not as clear as "germany" and const string enums can be very clarifying, while still compiling to the raw string, avoiding any overhead.
But partly, it's an expectations thing. const string enums are part of typescript, they're useful and not having them is surprising to developers (even if the reason for their current absence is reasonable). It also means, for example, babel-node can't be a drop-in replacement for ts-node, which is a shame.
I came across an issue related to this today. Imagine if you are in a monorepo project that has firebase, react-native and types packages.
Using normal enum causes issue inside firebase package if you use their serverless functions that run npm install during deployment. Since enums are transpiled to something like
const types_1 = require("@MyApp/types");
// ...
types_1.SIZE.BIG
and @MyApp/types is not available on npm, we get an error. Using const enum solves this as it is now transpiled to just "BIG"
But now, we get an issue in react-native project that uses babel to transpile typescript unless we add additional step to run typescript compiler, which feels like an overkill for this one feature. Falling back to type SIZE = "BIG" | "SMALL" | "MEDIUM" is only solution now, but we loose nice auto complete and devs will always need to spell these string correctly.
Prior art (Sucrase): https://github.com/alangpierce/sucrase/issues/423#issuecomment-467085280
For example, ... const enums are treated as regular enums rather than inlining across files.
Is it possible to write a babel plugin to run before transform-typescript that removes the const before enum? You can have the plugin only run in development. I don't know how to write babel plugins, but converting all const enum into enum sounds simple. I'll try looking into in it my free time.
I went through the babel-handbook. It's pretty simple. The const is just controlled by a boolean in the AST:
Node {
type: 'TSEnumDeclaration',
start: 159,
end: 203,
loc:
SourceLocation {
start: Position { line: 7, column: 0 },
end: Position { line: 10, column: 1 } },
const: true, // BOOLEAN HERE
...
Just traverse the whole AST and set them all to false.
Here is what I have so far (you guys can run it in node). I still need to read the part on how to create a plugin.
$ npm install @babel/parser @babel/traverse @babel/types @babel/generator
// const-enum.js
const parser = require('@babel/parser');
const { default: traverse } = require('@babel/traverse');
const types = require('@babel/types');
const { default: generate } = require('@babel/generator');
const code = `
const enum Enum {
A = 1,
B = A * 2
}
`.trim();
console.log(`Before:
${code}
`);
const ast = parser.parse(code, {
sourceType: 'module',
plugins: ['typescript']
});
traverse(ast, {
enter(path) {
if (types.isTSEnumDeclaration(path.node, { const: true })) {
path.node.const = false;
}
}
});
console.log(`
After:
${generate(ast).code}
`.trim());
$ node const-enum.js
You can experiment here: https://astexplorer.net/
It's a playground where you can create your own plugin and use it online in real time.
@nicolo-ribaudo I saw that site, but I don't see TypeScript support, only JavaScript. The plugin has to be a TypeScript to TypeScript transpiler. So then you can feed that into @babel/preset-typescript.
You can click on the gear next to the parser (in the header), and enable TypeScript support for Babylon 7. (which is the old name of @babel/parser)
@nicolo-ribaudo, using JavaScript + babylon7, I get Unexpected reserved word 'enum' (1:6) for the following code:
const enum Enum {
A = 1,
B = A * 2
}
I tried on the babel site and noticed that enabling preset-typescript doesn't do anything. I need to manually add plugin-syntax-typescript to allow enum syntax and plugin-transform-typescript to transform it to JS.
The only combinations that work on AST explorer are JavaScript + one of the following:
The only transformer that works is tslint. babelv7 transformer doesn't work with any parser. It complains about enum.
I wonder why I don't have any problems in node, without having to enable plugin-syntax-typescript.

Did you click on the gear next to "babylon7" and enable it's TypeScript plugin?
Oh, my mistake. I misunderstood the UI and what you said earlier. I thought parser dropdown and the gear was the same thing. Didn't notice the gear was separate settings for the parser. Thanks!
Hmm, now I have the parser working, but not the transformer:

Seems like their list of plugins for their babelv7 transformer doesn't have 'typescript'. Their parser has 'typescript' as an available plugin, as seen in the settings gear of the UI.
Actually, I figured out a workaround. I just cloned their repo to run locally with modified code. I changed 'flow' to 'typescript' since both can't be enabled together.
Now everything works:

Yay! I finished my plugin, babel-plugin-const-enum.
Add to your .babelrc:
{
"plugins": [
"const-enum"
]
}
The default is to remove the const keyword.
It also has a transform for #8741 if you use the option "transform": "constObject".
The README has more information.
You can test at the online repl by adding the plugin on bottom left.
Feel free to file issues or send in PRs at the GitHub Repo. This is my first time writing a plugin so there are things that can be improved.
Really awesome @dosentmatter !
@ForbesLindesay, considering there are some tradeoffs / footguns with the approach in your original description, is this plugin enough to close this issue?
Maybe along with some documentation about const enums and this plugin in some FAQ or something in the Babel TypeScript docs?
Yes, we should definitely link it in our docs.
Docs are updated in master now
Why is removeConst the default? What are the tradeoffs? (considering the transform-typescript doesn't typecheck anyway)
Why is
removeConstthe default? What are the tradeoffs? (considering thetransform-typescriptdoesn't typecheck anyway)
@qm3ster, the reason I chose removeConst to be default is because it relies on regular enum transpilation of @babel/plugin-transform-typescript and it is just the simpler solution.
I didn't want the default to be constObject, because that may be unexpected for developers. They have to wrap their head around why the constObject works, when they may just want const enum to work and not care about how or why. I left constObject for more "advanced" users.
Although, constObject should still work correctly without a minifier to inline enum access.
What's the status on this? Would really like to use const enums in my TypeORM enum columns
@vegerot, I saw you commented https://github.com/babel/babel/issues/8741#issuecomment-624234808 and you got a reply on that issue to use babel-plugin-const-enum. That issue is talking about using constObject option of babel-plugin-const-enum. If you want to removeConst, babel-plugin-const-enum allows you to do that. See https://github.com/babel/babel/issues/6476#issuecomment-509039884.
If you need the plugin to only run on TS files, you can use the babel-preset-const-enum. I am the author of the plugin so you can file an issue if you have any problems.
Thank you. I'll look into it
Most helpful comment
The reason is that const enums are a useful optimisation in production, and I can handle them by just using the full typescript compiler for the prod build.
In development. I want to make the build as fast as possible by using babel for everything and compiling each module independently.