6 ms·
An ode to TypeScript enums
- bubblyworld 2y agoOne thing I find useful about enums is that they can be used as both types and values, which is ergonomic for decorator-based libraries (like class-validator, nestjs, mikro-orm, etc). The best approach I've found in union land is using const assertions and typeof, which I don't love. Agree with the author that in almost every other way unions are better though... they play much more nicely with the rest of the type system. I find it endlessly annoying that I have to refer to enum members directly instead of just using literals like you can with union types.
- disintegrator 2y ago> One thing I find useful about enums is that they can be used as both types and values Makes sense. You can emulate that behavior by having an object literal with const assertion AND a union type of the same name derived from the object literal.
- bubblyworld 2y agoRight, yeah - this is what I meant by const and typeof. It's definitely an option, but I'm nervous of relying on the semantics of const like that. But maybe I shouldn't be, it seems pretty idiomatic? (the typeof part is just so you don't repeat yourself, or did you have something else in mind?)
- wruza 2y agoParameter properties also gone? I only recently found out about these, so useful for data-ish classes.
- disintegrator 2y agoIn principle you’ll still be able to use all of the features that existed before this flag but you’ll need to compile the code if targeting Node.js. I do think that this new flag is going to draw people away and we’ll probably see a bunch of tsconfig presets and boilerplate projects setting it to true. If you’re using a bundler then your’re not to going benefit from it in the medium term. It’s possible this will unlock faster build times with them in the future.
- lelandfe 2y ago> TypeScript 5.8 is out bringing with it the --erasableSyntaxOnly flag TypeScript sure loves the "our only documentation lives in the changelog" approach to stuff, huh? - The on-site Algolia search returns 0 results for "erasableSyntaxOnly" - The blocked-from-search release notes[0] looks like actual documentation - but urges to check out the PR[1] "for more information," despite the PR description being essentially blank. - The CLI options page[2] describes it thus: "Do not allow runtime constructs that are not part of ECMAScript," with no links to learn more about what that means. Edit: actually, I take it back! Clicking the flag on the CLI page takes one to an intimidating junk drawer page... but my issues with discoverability stand: https://www.typescriptlang.org/tsconfig/#erasableSyntaxOnly https://www.typescriptlang.org/tsconfig/#erasableSyntaxOnly [0] https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-8.html#the---erasablesyntaxonly-option https://www.typescriptlang.org/docs/handbook/release-notes/t... [1] https://github.com/microsoft/TypeScript/pull/61011 https://github.com/microsoft/TypeScript/pull/61011 [2] No idea why on-site search doesn't pick this up: https://www.typescriptlang.org/docs/handbook/compiler-options.html https://www.typescriptlang.org/docs/handbook/compiler-option...
- Tadpole9181 2y agoIt happened within hours, it can take a bit to update the docs and then get them reindexed.
- MBCook 2y agoIt was released late last week wasn’t it?
- deleted 2y ago[deleted]
- homebrewer 2y agoconst enums are almost never mentioned by these articles for some reason. They give you the best of both worlds: they're fully erasable, and have good LSP support (do no need to search for strings and bump into false matches — or even worse, for numbers).
- furstenheim 2y agoLack of LSP support looks really bad in the article proposed solution :/. But const enum seems to have several pitfalls. https://www.typescriptlang.org/docs/handbook/enums.html https://www.typescriptlang.org/docs/handbook/enums.html
- krona 2y ago> const enums are almost never mentioned by these articles for some reason. I think it's because a lot of tooling (excepting TSC) doesn't support cross-file const enums. But I agree - it's one of the reasons I started using TypeScript way back in 2013. I wouldn't be able to write comprehensible performance sensitive code without it.
- zdragnar 2y agoSeeing posts like these, I often feel alone preferring enums to string unions. There are certain situations where refactoring a string in a union will not work but refactoring an enum will. I don't want to type strings when, semantically, what I want is a discrete type. I don't even care that they become strings in JS, because I'm using them for the semantic and type benefits, not the benefits that come with the string prototype.
- forty 2y agoThat precisely one of my problem with enum: almost all TS type is structural typing, why have this exception enums being nominal typing?
- Tade0 2y agoAnd to add to the confusion Template Types let you compare enums as if they were strings.
- zdragnar 2y agoClasses aren't interchangable, excepting using a child when a parent is called for. Likewise, enums represent a discrete and unique set. The fact that there is either a number or a string used under the hood is irrelevant. I imagine using numbers or strings was useful for interop with vanilla JS (where JS needs to call a TS function with an enum as an argument), so it makes sense to use it instead of Symbols, which is what I typically pretend enumd are.
- bubblyworld 2y agoInteresting point about semantics. I wish there was a way to get the best of both - discrete type (correct semantics) but one that is auto inferred from literals in contexts where the type system expects it (ergonomics of use). Perhaps there are good reasons that doesn't work though, I haven't thought through it much =P
- eyelidlessness 2y agoYou’re not alone! I’ve given up the preference on team projects for pragmatic reasons, but the semantics of (string) enums are still my personal preference.
- forty 2y agoThis is my preferred home made way of doing "Enum" in TS theses days https://gist.github.com/forty/ac392b0413c711eb2d8c628b3e769896 https://gist.github.com/forty/ac392b0413c711eb2d8c628b3e7698... - it includes syntax to migrate from TS enum. The member documentation point is a good one, I'll look what can be done with my solution.
- spankalee 2y agoConst objects really are better than enums, in every way except declaration brevity. They're erasable syntax, so they work in environments that just strip types. Their emit is just what you write without the types. They can be composed in a type-safe way with standard JS operations. You can still write JS docs for values, deprecated the, mark them as internal, etc. type ValueOf<T> = T[keyof T]; const Foo = { /** * A one digit * @deprecated */ one: '1', two: '2', three: '3' } as const; type Foo = ValueOf<typeof Foo>; const Bar = { blue: 'blue', } as const; type Bar = ValueOf<typeof Bar>; // You can union enum objects: const FooOrBar = {...Foo, ...Bar}; // And get union of their values: type FooOrBar = ValueOf<typeof FooOrBar>; const doSomething = (foo: Foo) => {} // You can reference values just like enums: doSomething(Foo.two); // You can also type-safely reference enum values by their // key name: doSomething(Foo['two']); Given the TypeScript team's stance on new non-erasable syntax, I have to think this is how they would have gone if they had `as const` from the beginning. Ron Buckton of the TS team is championing an enum proposal for JS: https://github.com/rbuckton/proposal-enum https://github.com/rbuckton/proposal-enum Hopefully that goes somewhere and improves the declaration side of thigns too.
- hassleblad23 2y agoIts a shame because I like the enum way of declaration a lot more. `const Foo = { Bar: 'bar' } as const` - this just feels a bit weird.
- spankalee 2y agoThat's a taste thing. Personally, I like my TypeScript as a superset of JS with types, so I dislike all the custom value-space syntax. `const Foo = { Bar: 'bar' }` is how I would write an enum-like object in JS, so that's how I want to write it in TypeScript, just with added types.
- DidYaWipe 2y agoYes, why do we have "const" at the beginning AND end?
- deleted 2y ago[deleted]
- DanielHB 2y agoGeneral programming languages theory question, is one supposed to iterate over enum entries or is that considered an antipattern? I have found myself needing to do that a few times and it always felt a bit dirty.
- panstromek 2y agoI wouldn't say it's an antipattern. E.g. listing all possible values of some user input field is a pretty natural use case for that.
- williamdclt 2y agoNo that’s fine and a reasonable thing to do . In fact, I’d say it is one of the main points of enums and one of my biggest gripes against Go is the lack of that capability
- MBCook 2y agoWhat do people find works better as a string enum replacement? const Thing { one: “one”, two: “two”, three: “three” } as const Or just type Thing = “one” | “two” | “three” I’ve been thinking of getting rid of the simple string enums I have but it’s not clear to me why one is preferred over the other by people.
- braebo 2y agoIf all you need is a union type then the latter is plenty. If you need the actual strings to iterate over or validate against, deriving the value from an const array is helpful: const THINGS = ['one', 'two', 'three'] as const type Thing = THINGS[number]
- pcthrowaway 2y agoIf you want to be able to use the syntax Thing.one or Thing.two while having each refer to a discrete symbol, you should probably use Symbols so: const Thing = { one: Symbol(), two: Symbol() } as const; will prevent anything equality matching that isn't intentional
- o11c 2y agoThe problem with "just use literal strings/numbers" is that that's exactly the opposite of type safe. With them it is impossible to specify an argument of type `myenum | number | string`, despite that being commonly desired in some form. When targeting javascript, it seems to me that the obvious approach is to use symbols for enums. But symbols have a lot of WET. (of course, typescript's safety is unfixably broken in numerous other ways, so why bother?)
- motorest 2y agoCan anyone explain why enums are somehow bad but literal unions are supposed to be good? I'll be blunt: at the surface level, it looks like literal unions are something that only someone with an irrational axe to grind against enums would ever suggest as a preferable alternative just to not concede that enums are fine. If the problem lies in the low-level implementation details of enums, I cannot see any reason why they shouldn't be implemented the same way as literal unions. So can anyone offer any explanation on why enums should be considered bad but literal unions should be good?
- moogly 2y agoTypeScript enums require codegen, which won't work in a type erasure world. This is explained in the article.
- DidYaWipe 2y ago"Probably my favorite argument in steelmanning enums" Whatever that's supposed to mean.
- crummy 2y agoSteelmanning is the opposite of strawmanning. In other words, it's making the strongest version of an argument for the opposing side of the argument. The author doesn't like enums but is talking about their best attributes.
- DidYaWipe 2y agoThanks for the reply. I read a lot, and I've never encountered this term before. Seems like "in defense of" would be every bit as good, and universally understood.
- swatcoder 2y agoIt's an internet term of recent origin, from a specific community, not a traditional one. It's good to be familiar with the word, as it comes up in adjacent communities like this one, but like with most slang, there are indeed clearer ways to say the same thing. But also, some people don't realize that they've picked up a slang term or that people outside their community are part of discussions like we have here, so it comes up a lot. Now that you've spotted it, you'll likely see it here a lot. (FWIW, I hate it and am grateful that nobody can see me roll my eyes when its used. Same for "motte and bailey" and other comically pseudo-erudite slang from those folks)
- DidYaWipe 2y agoThanks. Glad to hear from someone else who despises this kind of douchily obscure jargon. Its smug adherents love to "flag" anyone who calls it out here, or calls out similarly douchey posts whose titles lack any description.