FYI, this is now. Node 23.6 will just run typescript files than can have their types stripped https://nodejs.org/en/blog/release/v23.6.0#unflagging---expe....
There is a seperate --experimental-transform-types flag which'll also transform for enums, but no idea if they ever intend to make this not experimental or unflagged.
its 2025 and node is finally good :)
The only other twist to import syntax is marking type-only imports with the type keyword so that those imports can be completely ignored by simple type removers like Node's. You can turn that check on today in Typescript's compile options with the verbatimModuleSyntax [1] flag, or various eslint rules.
[1] https://www.typescriptlang.org/tsconfig/#verbatimModuleSynta...
export const MyEnumMapping = {
active: 0,
inactive: 1
} as const
export type MyEnum = typeof MyEnumMapping[keyof typeof MyEnumMapping];
So you have the names exposed, but the underlying type is the number.Personally I am definitely not skilled enough at typescript to come up with this on my own before seeing this thread so this was not even an option until now.
export type MyEnum = ValueOf<MyEnumMapping>;
TypeScript not having enough sugar in its built-in utility types is definitely a fair criticism.But more to the point, the above is not usually how you do enums in TS unless you have some very specific reason to want your values to be numbers at all times. There are some cases like that, but usually you would just let the values be strings, and map them to numbers on demand if that's actually required (e.g. for a specific serialization format).
type MyEnum = {
active: 0;
inactive: 1;
}
const MyEnum: MyEnum = {
active: 0,
inactive: 1,
}
const showAge = MyEnum.active;
const showPets = MyEnum.inactive;
It's slightly more duplication, but a lot more readable (imo) to those unfamiliar to utility types. TypeScript also enforces keeping them in sync. function doSomethingWithMyEnum(val: MyEnum[keyof MyEnum])
You could do `val: number`, but now you're allowing any number at all.Ultimately, the type syntax in TypeScript is a key part of the language, and I don't think it's unreasonable to expect developers to learn the basic typeof and keyof operators. If we were talking about something wonkier like mapped types or conditional types, sure, it might make sense to avoid those for something as basic as enums.
Edit: But for what it's worth, yes, the above is still more elegant than enums. The syntax may feel less elegant, but among other things, the above does not depart from the structural type paradigm that the rest of the language uses, all for something as simple as an enum.
const MyEnum = {
x: 1,
y: 2,
z: 3,
// etc
}
instead of enum MyEnum {
x = 1,
y,
z,
// etc
}
when you want a series of constants each with a unique value but don't particularly care what that value is.TypeScript's enums are particularly weak compared to enums in other languages precisely because there's no JS support for enums. Modern languages have support for ADTs.
In the vast majority of cases, there's no good reason to do that.
Edit: But no, the reason enums in TypeScript suck is not that JS doesn't have them. That wouldn't fix anything other than the type stripping problem. The main reason that they suck is that they use a completely different type model from the rest of the language.
90% of the enums I use are regular integer enums. I don't get much use out of string enums, as you say union types do that job just fine.
I'm able to do that just fine in VS Code / Cursor.
I set up a union like this:
export type TestUnion = 'foo' | 'bar' | 'baz';
Then use it in another file like this: const bar: TestUnion = 'bar';
const barString: string = 'bar';
If I select 'bar' from the type and choose "Go to references", it shows me the `const bar` line, but not the `const barString` line, which is what I would expect.How is that the conclusion you reach? The proposal you link says types will be treated like comments by the runtime, so it's not about adding types that will be used in the runtime (which begs the question, why even add it? But I digress), but about adding types that other tooling can use, and the runtime can ignore.
So assuming the runtime will ignore the types, why would using enums specifically break this, compared to any other TypeScript-specific syntax?
If you ask me, both features have been de facto deprecated for ages now.
The future has been here for a while.
Maybe library compatibility?
My first reaction is that this just further fractures the ecosystem, where some codebases/libraries will have TS that is required to be compiled and some will not, adding a third kind of TS/JS code that's out there.
We’re not talking about the distant future. Node shipped its first version supporting type stripping six months ago.
That I would imagine there are other features being proposed that will continue to develop this compatibility?
Seems weird to me to decide you're OK with the build step and all the other complexity TS adds, but using enums is too much, because maybe in the future JS runtimes might be able to strip away types for you without a build-step.
But we all have different constraints and use cases, I suppose it does make sense for what you're building.
Parameter properties are I think still used quite heavily in Angular, but I don't see them much elsewhere. Again, they're a very old feature, and they don't play well with newer developments in the language (such as native private attributes), so it doesn't seem like much of a problem to avoid them as well.
The other big TS-only feature is old-style decorators, but that shows the danger of relying too much on this TS-based syntax sugar. Decorators have gone through several revisions, and the version that Typescript implemented is long dead. But a number of codebases are still stuck using this legacy system because it's not compatible with the newer versions of decorators that will (eventually, hopefully) be implemented in browsers. The legacy system is still maintained, I believe, and you can still keep on using it, but you'll not get the benefits of using the same system as the wider Javascript ecosystem, and you'll not get the benefits of having the syntax be native to browsers, when that happens.
In general, Typescript works best when you use it as simply a type annotation syntax for Javascript, and not as an additional layer of sugar on top of that. And clearly the Typescript developers see things similarly, because they've stopped implementing sugar-like features and have committed to only implementing the stuff that will also be implemented as new features in Javascript.
Parameter properties and namespaces, on the other hand, are kind of wonky TS specific features that nobody expects to be there unless they specifically find them in the docs. Parameter properties offer a little bit of conciseness but don't fill a pressing need. Namespaces solve a problem that just doesn't actually come up that often, and few devs are going to go actively looking for them. Even the example code on typescriptlang.org doesn't show a very compelling case; they show "Validation" used as a namespace for classes that are already namespaced the low tech way by having "Validator" in their names. (Which isn't to say that the feature isn't ever useful; it's just that cases where someone would actually seek it out are niche.)
All of that is to say that sure, enums are just one of a handful of features that will break in runtimes that simply strip out types, but they're also the main feature that's likely to trip people up.
> maybe in the future JS runtimes might be able to strip away types for you without a build-step.
You can't backpedal from that to "speaking about the specification". It's not future JS runtimes. It's a thing you can take advantage of right now.
Not just the future. Node shipped this with an "experimental flag" in the last LTS and in Current it works without the flag (will ship in the next LTS without a flag). Deno has done something like this for years now, and Bun for nearly as long. The remaining question is if/when Browser support might also exist, which for now remains at Stage 1 discussions with the technical committee (TC-39).
> Since Node.js is only removing inline types, any TypeScript features that involve replacing TypeScript syntax with new JavaScript syntax will error, unless the flag --experimental-transform-types is passed.
> The most prominent features that require transformation are:
> Enum > namespaces > legacy module > parameter properties
Or don't use TypeScript / JavaScript at all and even Node.js and be done with this language. Especially in the backend. Full stop.
The language itself is not only a bolt-on superset on top of another horrific language (JavaScript), it is still gives the programmer ways to shoot themselves in the foot and still has a unsound type system compared with the likes of Kotlin and Go.
This is even before talking about the still immature ecosystem around it.
> Enums is going to make your TypeScript code not work in a future where TypeScript code can be run with Node.js or in browser when typings are added to JavaScript[1]
There you go. TypeScript will just become like CoffeeScript and will be replaced with ...JavaScript (with type annotations).
It really says a lot about how immature it is especially for backend and just by even hearing the complaints about TypeScript 'enums' tells me all I need to know.
So what other foot-guns have the JS / TS ecosystem have hidden?
https://github.com/tc39/proposal-type-annotations/commits/ma...
Apparently they're planning on adding a tsconfig option to disallow these Node-incompatible features as well [1].
Using this limited subset of TS also allows your code to compile with Bloomberg's ts-blank-space, which literally just replaces type declarations with whitespace [2].
[1] https://www.typescriptlang.org/tsconfig/#verbatimModuleSynta...
[2] https://www.typescriptlang.org/tsconfig/#isolatedModules
It would essentially help you produce TypeScript that's compatible with the --experimental-strip-types flag (and ts-blank-space), rather than the --experimental-transform-types flag, which is nice because (as someone else in this thread pointed out), Node 23 enables the --experimental-strip-types flag by default: https://nodejs.org/en/blog/release/v23.6.0#unflagging---expe...
[1] https://www.typescriptlang.org/docs/handbook/2/classes.html#...
Enums is going to make your TypeScript code not work in a future where TypeScript code can be run with Node.js or in browser when typings are added to JavaScript[1]
Enums results in runtime code and in most cases you really want type enums. Use `type State = "Active" | "Inactive"` and so on instead. And if you really want an closed-ended object use `const State = { Active: 1, Inactive: 0 } as const`
All of the examples in the article can be achieved without enums. See https://www.typescriptlang.org/play/?#code/PTAEFEA8EMFsAcA2B...
[1] https://github.com/tc39/proposal-type-annotations