Not to mention that you can add documentation to each of the entries.
Not to mention that you can add documentation to each of the entries.
type SomeProcessState =
// when waiting for user interaction
| 'idle'
// busy processing
| 'busy'
// finished processing
| 'success'
const OtherProcessStates = Object.freeze({
/**
Waiting for user interaction
*/
Idle: 0,
/**
Busy processing
*/
Busy: 1,
/**
Finished processing
*/
Success: 2,
} as const)
type OtherProcessState = typeof OtherProcessStates[keyof typeof OtherProcessStates]
The second form those are even working JSDOC comments.For example, if you already depend on package foo (depending on package baz@^1.0.1, resolving to 1.0.1) and then add a dependency on package bar (depending on package baz@^1.0.2, resolving to 1.0.3), then the same enums from package baz from the two transitive imports are not compatible, since they are not the exact same instance. So TS won't accept a baz enum returned from foo being passed to a function in bar expecting a baz enum. In this example you could fix it by letting the package manager "dedupe" your lockfile since foo could actually happily also use baz@1.0.3. But if the ranges are incompatible, your best hope is aligning resolutions/overrides. Or fall back to forking and patching packages.
And if you're writing your own library interfacing with baz enums, you have to include the full exact version of the originating package to get the right reference. So if baz also has 200MB of total dependencies, you can't opt out of those if you want to reference that 10-line enum in a function signature. As opposed to interfaces and const-types, which you can just vendor (copy-paste exactly what you want) and TS figures it out. You could break the type out to a subpackage. Not so with enums.
If you want to extend a union type, you can just add your own with the new element and it will still typecheck. You can not with enums. So you resort to encapsulation or ad-hoc conversion-functions, which gets frustrating and messy very quickly.
This is only a concern with enums (and classes, where there is good reason for it as the implementation does matter at runtime in a way that enum primitive values do not). The alternatives don't have this issue - as they are structurally typed, TS will "merge" them at type erasure.
If you are 100% sure that your enums stay private inside your module and are never exposed via references in public APIs, at least you're mitigating much of this. But why paint yourself into that corner? (at the point that readability of comments is a concern I suspect you need to reconsider)
I think the point is that enums aren't real in Typescript because enums aren't real in JS. They are fakes that generate a bunch of JS code that you are better off writing by hand (such as examples above and elsewhere, or with libraries like io-ts or Zod or other great options). They are fakes that exist mostly because of a desire for backwards compatibility with the type system of Typescript < 1.0, which also means they are increasingly out of touch with modern types.
You used a lot of words to say syntactic sugar.
People love syntactic sugar. It solves their problems and makes their life easier.
People do love syntactic sugar. That's why it is important to point out why sometimes it is table salt and they should avoid trying to put it on their desserts.
They aren't. As others already pointed out, enums greatly simplify some usecases, although they brush the complexity of implementing them under the rug.
Because neither TFA nor this thread has yet proposed anything that isnt better served by later introduced (say TS 3~4.x+) language features or even external libraries if you actually do want to use them for their generated runtime library (enums) and not just their type-aspects.
I have only seen them complicate things.
You could use it to make typescript prevent forking and code-duplication within a codebase of engineers you dont trust, I guess. But it doesn't seem like a great use either.
Right, for that you are better off using unique Symbols. That's definitely the modern JS way to nominally type something in a private way that stays inside your API boundary and is very much more useful because it is also enforced at runtime, too, and not just an accident of old type mechanics.
Yes, I've got eslint errors turned on disallowing any use of enums at this point, because I see it as a code smell/risk, but that doesn't mean I'm going to take away the feature from your codebase, that's up to you and your judgment.
You can do that with either other solution.