Enums considered harmful

Matt Pocock
15 Dec 202209:23

Summary

TLDRThis video discusses the controversial use of TypeScript enums and their peculiar behavior at runtime. The speaker explains why enums, though initially introduced for a C#-like feel, may not be the best choice due to their unpredictable nature and the TypeScript community's shift towards 'as const' objects for type safety and flexibility. The video also touches on the potential of JavaScript adopting enums natively, which could change the current perspective on their usage.

Takeaways

  • 📝 TypeScript enums are a feature that has received criticism for not being native to JavaScript and for their unpredictable behavior at runtime.
  • 🔄 Enums in TypeScript can be confusing because they don't behave as expected; they map both keys and values to the same object, which can lead to unexpected results when using `Object.values`.
  • 🛠 Anders Hejlsberg, the lead architect of TypeScript, has expressed that if TypeScript were being created today, enums might not be included due to their issues.
  • 🔑 TypeScript enums can be useful for type safety and refactoring ease, but they require careful handling due to their nominal typing nature, which is unusual for TypeScript's structural type system.
  • ⚠️ Using enums can lead to errors, especially when different enums with the same values are used interchangeably, as TypeScript cares about the names of enum members.
  • 🌟 'const enums' in TypeScript are a compromise, providing type safety without the runtime object structure, but they have their pitfalls and are not recommended for library code.
  • 📚 The TypeScript documentation warns against the use of 'const enums' due to potential issues with compiler control and edge cases.
  • 📦 An alternative to enums is using a 'const' annotated plain object (POJO), which provides a cleaner and more flexible approach, aligning with TypeScript's structural typing system.
  • 🔄 Using 'as const' objects allows for more flexibility and can represent mappings between semantic keys and their representations, making it a powerful feature for various use cases.
  • 🚫 The speaker suggests ignoring enums in favor of 'as const' objects, and even encourages flagging enums in PRs for reconsideration.
  • 🔄 If enums were to be included in JavaScript natively, it could change the conversation, as they would be better documented and understood by all JavaScript developers.

Q & A

  • What is the main topic discussed in the video script?

    -The main topic discussed in the video script is the use of TypeScript enums, their pros and cons, and whether they should be used or replaced with alternative approaches.

  • Why do some people criticize TypeScript enums?

    -Some people criticize TypeScript enums because they behave unpredictably at runtime and are not native to JavaScript, which can lead to confusion and unexpected behavior in code.

  • What is the difference between a regular enum and a const enum in TypeScript?

    -A regular enum in TypeScript creates an object at runtime with both key-value pairs and reverse mappings, while a const enum only exists at the type level and is stripped out at runtime, providing type safety without a runtime representation.

  • Why might TypeScript enums be considered a mistake by the language's creators?

    -The creators of TypeScript might consider enums a mistake because they do not align well with TypeScript's structural typing system and can introduce complexity and confusion in the code.

  • How do enums behave differently when used as a switch case in TypeScript?

    -Enums in TypeScript, when used as a switch case, require the use of the enum member with the enum name prefix, such as `logLevel.debug`, rather than just the value `debug`, due to TypeScript's nominal typing for enums.

  • What are the benefits of using enums for refactoring in TypeScript?

    -Enums make refactoring easier in TypeScript because they allow for easy renaming and reordering of enum members, which are automatically reflected throughout the code, improving developer efficiency.

  • What is the recommended alternative to using enums according to the video script?

    -The video script recommends using a 'const' annotated plain old JavaScript object (POJO) as an alternative to enums, which provides type safety and flexibility without the complexities associated with enums.

  • Why are const enums considered dangerous in library code?

    -Const enums are considered dangerous in library code because they can lead to runtime errors if the compiler does not properly handle their inlining, and they can cause confusion due to their absence from the runtime.

  • How does using 'as const' provide more flexibility compared to enums?

    -Using 'as const' allows for the creation of objects with keys that can be used directly as types, providing a more natural and flexible way to handle type-safe values without the need for enum declarations and additional mappings.

  • What is the potential impact of enums being natively included in JavaScript?

    -If enums were natively included in JavaScript, they would be more predictable and better understood by developers, as they would be a standard part of the language with comprehensive documentation.

  • What is the presenter's stance on teaching enums in his TypeScript course?

    -The presenter does not teach enums in his TypeScript course, indicating his low regard for their use and suggesting that they are not essential for mastering TypeScript.

Outlines

00:00

🤔 TypeScript Enums: Pros and Cons

The video script discusses the controversial topic of TypeScript enums, questioning whether they should be used or not. It explains that enums in TypeScript are not native to JavaScript and were introduced to give TypeScript a C-sharp-like feel. However, the speaker mentions an interview with Anders, who suggests that if TypeScript were to be created anew, enums might not be included due to their unpredictable behavior at runtime. The script also covers const enums, which disappear at runtime but are inlined where used, offering a compromise between type safety and runtime clarity. The paragraph concludes with the suggestion that enums might be overcomplicated for their use case and that their benefits might not outweigh the simpler alternatives available.

05:00

📘 Alternatives to TypeScript Enums: As Const Objects

The second paragraph explores an alternative to TypeScript enums, which is using 'as const' with plain old JavaScript objects (POJOs). This approach is favored by the speaker for its simplicity and flexibility, allowing for easier refactoring and a more natural fit with TypeScript's structural typing system. The 'as const' annotation ensures immutability, and the speaker demonstrates how to extract types from such objects for use in functions. The benefits of using 'as const' include reduced code complexity and the ability to map semantic keys to their representations, which can be applied to various use cases beyond logging levels. The speaker concludes by recommending against the use of enums in favor of 'as const' objects and suggests monitoring their usage in codebases. The paragraph ends with a plug for the speaker's TypeScript course, 'Total TypeScript,' and a call to action for viewers to like the video to support the channel.

Mindmap

Keywords

💡TypeScript

TypeScript is a superset of JavaScript that adds optional static typing to the language. It is designed for the development of large applications and transcompiles to JavaScript. In the script, TypeScript is the main topic of discussion, especially its feature of enums, which are used to define sets of named constants.

💡Enums

Enums, short for enumerations, are a way to give more friendly names to sets of numeric values. In TypeScript, they are used to define a set of named values where each name is associated with a number. The script discusses the use and controversy around TypeScript enums, noting that they are not native to JavaScript and can behave unpredictably at runtime.

💡Const Enum

A const enum in TypeScript is a type of enum that does not exist at runtime; it is only used for type-checking during development. The script mentions const enums as an alternative to regular enums, which have their values stripped out at runtime but are inlined where used, providing type safety without runtime overhead.

💡Object-Oriented

Object-oriented programming is a paradigm based on the concept of 'objects', which can contain data in the form of fields and code in the form of methods. TypeScript was influenced by languages like C# and introduced enums to give a similar object-oriented feel. The script refers to this influence when discussing the origin of TypeScript enums.

💡Transpilation

Transpilation is the process of converting code from one programming language to another, often from a newer or less compatible version to an older or more widely supported version. The script uses the term to describe how TypeScript enums are converted into JavaScript objects during the compilation process.

💡Structural Typing

Structural typing is a type system where types are checked based on their structure or the shape of the data they hold, rather than their explicit names. TypeScript is described as having a structural type system in the script, which contrasts with the nominal typing introduced by enums.

💡Refactor

Refactoring is the process of restructuring existing code without changing its external behavior, typically to improve nonfunctional attributes such as readability or maintainability. The script mentions that enums in TypeScript can make refactoring easier due to their predictable behavior during such changes.

💡POJO (Plain Old JavaScript Object)

A POJO is a simple JavaScript object that does not have any special methods or properties added by a framework. The script suggests using a POJO with a 'const' annotation as an alternative to enums, providing a cleaner and more flexible approach to defining sets of named values.

💡Type Safety

Type safety refers to the practice of defining and enforcing the types of data that a program component can accept, helping to prevent certain types of bugs. The script discusses how const enums provide type safety by ensuring that only valid enum values can be used at compile time.

💡ECMAScript Proposal

An ECMAScript proposal is a suggestion for a new feature or improvement to the JavaScript language, which is reviewed and potentially adopted by the committee responsible for the ECMAScript standard. The script mentions an ongoing proposal to bring enums into JavaScript natively, which could change the conversation around their use.

Highlights

Discussion on TypeScript enums and their usage in scripting.

Controversy around TypeScript enums and their implementation.

Explanation of TypeScript enums syntax and their origin.

Anders Hejlsberg's view on the inclusion of enums in TypeScript.

Runtime behavior of enums in TypeScript and their unpredictability.

Difference between enum and const enum in TypeScript.

Const enums and their disappearance at runtime.

TypeScript enums and their impact on type safety and refactoring.

Critique of const enums and their pitfalls in TypeScript.

Alternative to enums using a POJO (Plain Old JavaScript Object) with const annotation.

Advantages of using 'as const' over enums in TypeScript.

Teaching approach to TypeScript and the exclusion of enums.

Potential for enums to be included in JavaScript and the implications.

Encouragement for viewers to support the channel by liking the video.

Invitation to engage in TypeScript debates and explore further topics.

Closing remarks and anticipation for future content.

Transcripts

play00:00

what's up Wizards we have a really

play00:02

interesting topic to talk about today

play00:04

which is typescript enums time script

play00:06

enums should you use them or should you

play00:09

do something different it's pretty

play00:11

fashionable to actually kind of bash on

play00:13

typescript enums themselves not the

play00:16

concept of enums but the particular

play00:18

keyword that you use in typescript to

play00:20

express them this is the piece of syntax

play00:22

I'm talking about enum log level and

play00:25

then you can have debug error warning

play00:26

you can put anything you want in here

play00:28

you can specify these with numbers

play00:30

manually if you want to or you can use

play00:32

strings like this there's also a version

play00:34

of an enum called a const enum which

play00:36

we'll look at in a bit as well so why do

play00:38

enums get so much Flack well the first

play00:41

thing is you might notice that the const

play00:43

enum thing or just enum by itself is not

play00:45

something that's native to JavaScript

play00:47

this was actually something that they

play00:49

introduced pretty early on in typescript

play00:51

and because they wanted to give a sort

play00:53

of like a c-sharp object-oriented

play00:55

feeling to typescript but I saw an

play00:58

interview with Anders recently and he

play01:00

said that if they had a green field they

play01:02

probably wouldn't make that mistake

play01:04

again like if typescript started you

play01:06

know today or last Monday or whatever

play01:08

they probably wouldn't put enums in the

play01:10

language enums behave a little bit

play01:12

unpredictably at run time so let's say

play01:15

we have this enum log level by default

play01:17

the value of debug is going to be zero

play01:19

warning is going to be one error is

play01:20

going to be two so naturally you kind of

play01:22

think that this log level is kind of

play01:24

going to end up like an object like this

play01:26

with debug 0 warning one error two but

play01:29

it actually doesn't if we look at the

play01:30

transpiled JavaScript here then this log

play01:33

level actually ends up as a slightly

play01:35

different object we can see this kind of

play01:37

funky syntax where it goes okay first we

play01:40

assign log level debug to zero then the

play01:44

result of that which is going to be zero

play01:46

we'll say log level zero equals debug

play01:48

and then it does that for each of them

play01:50

so what you end up with is actually an

play01:51

object that looks like this log

play01:53

underscore level where we have debug 0

play01:56

but also zero debug this means that if

play01:59

you you do object.values on a log level

play02:01

you're going to end up with zero debug

play02:03

one warning to error which if you're

play02:06

treating this like an object is totally

play02:08

not what you expect so this is the first

play02:10

annoying thing about enums is that they

play02:12

don't quite behave exactly as you expect

play02:14

them to this is slightly different if

play02:16

you were to use a string enum if you

play02:18

were to manually assign debug to debug

play02:20

warning to warning error to error then

play02:22

it just creates an object out of this

play02:24

but if you're doing this it sort of

play02:25

defeats the point of using an enum in

play02:27

the first place which is these things

play02:29

don't get automatically assigned sure

play02:31

they're fine but it's just a little bit

play02:33

iffy yeah I said iffy and this is an

play02:35

immediately invoked function expression

play02:37

which is an iffy so it's a joke

play02:41

not very good joke the way that you use

play02:43

an enum is kind of like this where you

play02:45

say Okay imagine you have a log function

play02:48

where it takes in a message and a level

play02:50

here you can specify this as kind of

play02:53

like any member of this enum this means

play02:55

that enum's sort of like switch over to

play02:58

the type world as well as live in the

play02:59

runtime World which is useful then you

play03:01

can call it with log level.debug you

play03:03

can't call it with like a member of the

play03:06

enum that isn't expressed as the enum so

play03:09

you can't just pass like debug here it

play03:11

has to be log level dot debug this is

play03:13

really weird because we know that

play03:15

typescript is like a structural type

play03:17

system meaning that it doesn't really

play03:19

care about the names of things only

play03:20

about their runtime values so surely it

play03:22

shouldn't care whether we pass debug or

play03:25

log level dot debug because the values

play03:26

are the same well enums break a little

play03:29

bit of a rule in typescript which is it

play03:31

does care about the names of things for

play03:34

enums typescript becomes a nominal type

play03:37

system it cares about either whether

play03:39

it's a log level enum or a log level 2 2

play03:42

enum here if I create log level and log

play03:44

level 2 even though the values are

play03:46

exactly the same I can't assign log

play03:49

level 2.debug to this level here now

play03:52

this does give you some benefits it

play03:53

means that you can do really nice

play03:54

refactors where you just sort of change

play03:56

all of the members around and it fits

play03:59

really well with vs code so people who

play04:01

love enums name this as their number one

play04:03

reason why they use them refactors are

play04:05

just so easy let's look briefly at const

play04:08

enums too so if we change this from log

play04:10

level to const enum log level then look

play04:14

at that look at the right hand side when

play04:16

I add this const then actually the enum

play04:19

itself disappears at runtime this means

play04:22

that the enum then only exists in

play04:24

typescript in the type level but this is

play04:27

kind of weird it's stripped out at

play04:29

runtime but then it's actually added in

play04:31

line wherever you use it this kind of

play04:34

seems like quite a good compromise

play04:35

really because it's you can't really

play04:37

access the runtime structure so it's not

play04:39

going to confuse you but it gives you

play04:41

all of the benefit benefits of type

play04:42

safety with your enums and if you like

play04:44

the refactors then the refactors are

play04:45

just as easy with as with normal enums

play04:47

however the typescript docks pretty much

play04:50

kind of say you probably shouldn't use

play04:52

this it has an entire section on all the

play04:54

pitfalls of constinence you should also

play04:56

never ever ever use them if you're

play04:58

inside Library code too because cons

play05:00

enums are really dangerous if you don't

play05:03

control the compiler that's emitting

play05:05

them basically all of these edge cases

play05:07

are enough to just scare me off

play05:09

recommending consonums because there's

play05:11

so many simpler Solutions the thing that

play05:13

I do instead of using enums and I've

play05:16

seen this all over the place in open

play05:18

source application code is using a pojo

play05:20

a plain old JavaScript object that's

play05:23

just got this as const annotation on it

play05:25

as const means that this object can't be

play05:28

manipulated or changed there's a little

play05:30

bit of type magic here to extract the

play05:32

type out so we can use it we're just

play05:34

extracting the object values and then

play05:36

this log level ends up as debug or

play05:38

warning or error so now we put level as

play05:40

log level and we can either pass log

play05:43

level.debug or just pass in debug itself

play05:46

for me this feels like a really natural

play05:48

way to do it because you don't need to

play05:51

actually import the log level every time

play05:53

you need to use it you can just pass in

play05:54

the value that you want to this really

play05:56

makes sense to me because I think of

play05:58

typescript as a structural typing system

play06:00

and this weird kind of nominal enum

play06:03

thing doesn't really fit with me so here

play06:05

because the only thing that log cares

play06:07

about is the value of debug we could

play06:08

exchange different enums and like it's

play06:11

just working at the type level how you

play06:13

expect it to work at the runtime level

play06:15

the other really cool thing about as

play06:16

const is it lets you be super flexible

play06:19

with the object that you create these

play06:21

keys in so here I've got a mapping

play06:23

between the levels and a human readable

play06:26

version of that level instead of the log

play06:28

level being extracted from the values

play06:29

it's now being extracted from the keys

play06:31

and in this log function we receive that

play06:33

log level and we index into the log

play06:36

level in order to get the debug warning

play06:39

or error thing here and that means we

play06:40

can pre-pend our message with the level

play06:43

that it's at when you think about this

play06:44

your brain just starts like exploding

play06:46

with all the possibilities here because

play06:48

I mean I've done this just dozens of

play06:50

times in application code where you have

play06:51

a mapping between like a semantic key

play06:54

you know an enum and the thing that you

play06:56

want that semantic key to represent this

play06:58

could be as diverse as you know variance

play07:00

in a component or you know screen sizes

play07:03

or languages all of these different

play07:04

things and being able to represent those

play07:06

in as const really makes it a killer

play07:09

feature over enums themselves to

play07:11

represent this same code with an enum

play07:13

you would need to declare the enum then

play07:15

have your titles map which has all of

play07:17

the inner mapping done there then

play07:19

extract the enum in there then use the

play07:21

enum inside the log function it's just

play07:22

like a lot more code and I think an as

play07:25

const does this a lot better A lot

play07:27

cleaner I think you should ignore enums

play07:30

I think you should forget they exist and

play07:32

I think you should flag in PRS that this

play07:36

should probably be an as const I don't

play07:38

think you should reject PRS that have

play07:40

enums in but I do think you should try

play07:44

to monitor when they go into your code

play07:46

base and see if they're really necessary

play07:48

I don't even teach enums in total

play07:51

typescript That's how little I think of

play07:53

them and total typescript by the way is

play07:55

my paid typescript course if you want to

play07:58

learn more about this stuff and become a

play08:00

typescript wizard you can have a look on

play08:01

total typescript.com one final thing

play08:03

here which is if enums ever make their

play08:06

way into JavaScript language as a whole

play08:09

I think that would be a different

play08:10

conversation there is an ecmascript

play08:12

proposal right now to bring enums into

play08:16

JavaScript natively I don't know if the

play08:18

implementation is slightly different to

play08:19

the way that typescript does enums but I

play08:21

know that if enums were in JavaScript

play08:23

then you'd be able to use them a lot

play08:26

more predictably because it would be so

play08:28

much better documented I mean obviously

play08:29

typescript has done a great job

play08:30

documenting these enums but they're

play08:33

special to typescript whereas if they

play08:34

were native to JavaScript everyone would

play08:36

understand them out of the box if you've

play08:38

enjoyed this video then do hit the like

play08:39

button I realized the other day why p

play08:41

people ask people to like hit the like

play08:44

button it's because it's an actionable

play08:46

thing that I can ask you to do to prove

play08:49

to YouTube that you think this channel

play08:50

deserves more viewers it costs nothing

play08:52

for you to do maybe other than to like

play08:54

remove full screen and like press it

play08:56

there it's like four clicks or something

play08:58

so if you want to do the minimum

play09:00

possible thing that you could possibly

play09:02

do to support me in this channel then

play09:04

just hit that like button and if you

play09:05

enjoy typescript debates you will love

play09:07

this video on types versus interfaces as

play09:10

well and which one should you use should

play09:12

we be forgetting all about interfaces no

play09:15

but should we be using types mostly

play09:17

probably yes thanks so much for joining

play09:20

Wizards I will see you soon

Rate This

5.0 / 5 (0 votes)

Etiquetas Relacionadas
TypeScriptEnumsJavaScriptDevelopmentType SafetyRefactorStructural TypingObject-OrientedC# InfluenceECMAScript Proposal
¿Necesitas un resumen en inglés?