Hacker News new | ask | show | jobs
by inbx0 1560 days ago
Offtopic but I have to ask now that this came up, why do people even use enums instead of string literal type unions? The latter just seems so much simpler and more readable to me. You get the same type safety, pretty much the same or better performance, autocomplete works fine etc.

One reason I've heard is that enums allow you to do rename symbol refactoring in all use sites, but like what How often do you need to do that? Once in a lifetime? Twice? And with string unions you can ofcourse also just rename it, TS will tell all the places that need fixing, and it'll take couple hours to go though 1000+ usages of it.

2 comments

Enums aren’t interchangeable, which is either a good thing or a bad thing, depending.

Let’s say I have an Icon component with props like:

    type IconProps = { size: “s” | “m” | “l” }
And then let’s say I have a Button component but there’s no small size:

    type ButtonProps = { size: “m” | “l” }
If I use string unions I can happy assign one size to another:

    const Button = ({ size }: ButtonProps) => (
      <button><Icon size={size} />
    )
If I used enums (ButtonSize and IconSize) then that would not be possible and TypeScript would complain.

So the question is: Is that a bad thing?

And the answer depends on your domain. If you have reason to believe that in your domain people are likely to map between two incompatible namespaces, then enums could help. And string unions could let through some bugs.

In my experience that’s never been a real risk so I prefer string unions.

The other thing enums can get you is you can rename the variable without changing the values. So if your strings are persisted to the database and you don’t want to change them, but you do want them to have a slightly new meaning, you could rename the enum key, and get the new name in code, while keeping the old string value.

I hear what you're saying but I'd rather not waste even those two hours ;)

I agree with what @erikpukinskis said but I'll also add that I just like the way they read. It immediately tells me that there is a limited number of potential values that can be put in a place. If I see some arbitrary string then I have to wonder, if only briefly, if any arbitrary string can be plugged in there.

    // What does addTask take? Days of the week?
    // Time-sensitive identifiers like you'd see in moment.js?
    // I guess I'll check the definition or mock out a call to see
    addTask('Sunday', newTask)

    // vs.
    // Ah, it takes a day
    addTask(Day.Sunday, newTask)
It's subtle and not super important but it makes me happy.