āļøĀ Are Enums Really Harmful?
Today I had a discussion with some friends at Theodo about Matt Pocockās video dramatically titled āEnums considered harmfulā. The video expresses Mattās views about how Typescript native enums are a big no-no and should essentially be avoided and discouraged.
Iām a big fan of the way Matt breaks down concepts and explains them clearly, BUT - there was clearly much to debate, with viewers expressing different opinions in the comment section. And so we sat down for 30 minutes to put an end to the question - have we been lied to, and are typescript Enums actually dangerous to use??
Ā
šĀ Points Raised in Video
Hereās a quick analysis of Mattās points from the video to start (points are simplified)
Typescript native enums look a bit like this
TS enums are transpiled to Javascript objects in an unintuitive way - they eventually look like this
So doing normal JS object manipulation can be annoying - for example, mapping over the keys of an enum, which in my opinion is a fairly common operation
How can we avoid this? One way is by using POJOs, or plain old javascript objects that look like this
The added const assertion just ensures the enum members are readonly
This is defined as a javascript object, and so no weird transpiling issues occur
Ā
šĀ Michaelās Opinion:
So āPOJOā enums seem to get around the object transpiling issue - why not just use these JS āenumsā all the time?
There is one major drawback - they allow for the usage of the enumās primitive values directly in code (such as
'apple'
above), instead of throwing type errors and demanding something like FruitEnumPojo.Apple
Mattās opinion on this is clearly stated in the video - since Typescript is technically a āstructuralā type system and not a ānominalā type system, this is exactly what we should expect, and how we should interact with our code. Structural type systems should not care about names, but only runtime values.
Ā
Structural type systems should be ok with using the enum values interchangeably with explicit references to the values through the enum keys like in the snippet above, and this makes for a more intuitive developer experience.
Ā
This is where I disagree! (Somewhat)
In my opinion, such interchangeable use of
"apple"
and FruitEnum.Apple
encourages the use of āmagicā values without a reference, and thus- Makes refactoring harder as there is no single source of truth, and each instance of
"apple"
must be modified if the enum memberās value ever changes
- May slightly reduce the readability of your code, because seeing
test("apple")
in your code raises many questions, such as: is āappleā definitely a valid argument? (A new developer may need to check that it is typed correctly and that the primitive values passed totest
are permitted)
Ā
āļø So, What is the happy middle?
My personal choice of enum are typescript string enums that look something like this
The usage of string enums in my opinions avoids all of the pitfalls that Matt references. The transpiled JS is intuitive and as expected, matching the output of the JS enum with const assertion (see below)
We also can be stricter about accessing enum members explicitly rather than magic values in code, with type errors being thrown otherwise.
Ā
Ā
šĀ Closing Thoughts
The tradeoffs between string enums and Mattās proposed POJO enums are centred around readability, maintainability and object manipulation, and can be seen below:
Ā
ć
¤ | Readability | Maintainability | Object Manipulation |
Typescript String Enums | š¢Ā | š¢ | š¢ |
POJO Enums | š”Ā Magical values raise questions about type safety, slightly impacting readability | š“Ā No single source of truth for enum member values makes refactoring harder | š¢ |
Ā
Overall, while Matt provides an interesting viewpoint on the use of Typescript enums, I am not entirely convinced. I believe string enums mitigate most of the drawbacks that Matt raises, and the drawbacks of their usage do not outweigh the benefits.
Ā
To his credit, Matt does give some leeway to typescript enums, saying while he wouldnāt actively encourage them he wouldnāt reject any pull requests based on their usage - and it should be noted that much of the conclusions above are to some extent down to developer preference. Matt may feel that the coercion of typescript into a nominal type system is far too jarring and that it produces a very negative and unintuitive developer experience.
Ā
And importantly, setting the standard of using string enums opens the doors for the risk of new or less experienced developers joining a project to accidentally use variants such as numeric and non-string typescript enums, which have a much more negative impact (such as the weird object transpiling issue we discussed earlier).
Ā
Therefore for each project, I would advise you take into consideration the experience level of the developers in conjunction with the benefits / risks tradeoffs discussed and come to an informed decision, with my personal preference in most cases being string enums.
Ā
If you disagree or have any further comments or questions about my opinions, please donāt hesitate to contact me directly - I would be happy to fill any personal knowledge gaps š
Ā