Enums are Bad

Pro Members Only

This lesson is available if you have an active subscription.

Alternatively, some member might be able to gift it to you.

If you already have a subscription, you can sign in.

Professional TypeScript Masterclass Lessons

1.Introduction
free
⏱️ 1:54
2.Setup
free
⏱️ 5:44
3.Primitive Types
free
⏱️ 1:42
4.Instance Types
free
⏱️ 1:52
5.Arrays And Tuples
free
⏱️ 1:38
6.Objects
free
⏱️ 1:33
7.const declarations
free
⏱️ 1:03
8.Function Types
free
⏱️ 1:57
9.Structural Typing
free
⏱️ 2:10
10.Classes in TypeScript
free
⏱️ 1:48
11.Target Compiler Option
free
⏱️ 2:37
12.Generics
⏱️ 3:02
13.Special Types any And unknown
⏱️ 2:00
14.JavaScript to TypeScript
⏱️ 1:32
15.Frontend Projects
⏱️ 3:49
16.Type Assertions
⏱️ 2:15
17.Type Casting
⏱️ 1:16
18.Modules
⏱️ 1:55
19.Type Declarations
⏱️ 4:25
20.Creating NPM packages
⏱️ 3:20
21.Async Await
⏱️ 3:05
22.Running in NodeJS
⏱️ 1:40
23.Lexical this
⏱️ 2:34
24.readonly Modifier
⏱️ 1:59
25.Union Types
⏱️ 2:57
26.Literal Types
⏱️ 2:58
27.Type Narrowing
⏱️ 4:19
28.Discriminated Unions
⏱️ 3:29
29.Class Parameter Properties
⏱️ 1:02
30.Strict Compiler Option
⏱️ 6:18
31.null vs undefined
⏱️ 4:19
32.Intersection Types
⏱️ 2:03
33.Optional Modifier
⏱️ 2:47
34.Non Null Assertion Operator
⏱️ 3:40
35.Interfaces
⏱️ 2:28
36.Interface Declaration Merging
⏱️ 1:01
37.Types vs Interfaces
⏱️ 2:16
38.never Type
⏱️ 3:00
39.implements Keyword
⏱️ 1:25
40.Definite Assignment Assertion
⏱️ 2:31
41.User Defined Type Guards
⏱️ 2:02
42.Assertion Functions
⏱️ 3:42
43.Function Overloading
⏱️ 4:15
44.Call Signatures
⏱️ 2:53
45.Abstract Classes
⏱️ 1:53
46.Index Signatures
⏱️ 3:08
47.Readonly Arrays and Tuples
⏱️ 2:58
48.Double Assertions
⏱️ 2:20
49.const Assertions
⏱️ 3:55
50.this Parameter
⏱️ 2:33
51.Generic Constraints
⏱️ 2:43
52.typeof Type Operator
⏱️ 2:12
53.Lookup Types
⏱️ 3:12
54.keyof Type Operator
⏱️ 3:55
55.Conditional Types
⏱️ 4:39
56.Contitional Types with Unions and never
⏱️ 3:32
57.infer Keyword and `ReturnType<T>`
⏱️ 3:47
58.Mapped Types
⏱️ 2:48
59.Mapped Type Modifiers
⏱️ 3:37
60.Template Literal Type
⏱️ 4:28
61.Partial<T>
⏱️ 1:27
62.Required<T>
⏱️ 1:36
63.Readonly<T>
⏱️ 1:34
64.Record<K, T>
⏱️ 4:05
65.Project References
⏱️ 4:18
66.undefined vs. optional
⏱️ 2:48
67.satisfies Operator
⏱️ 2:42
68.PropertyKey Type
⏱️ 0:57
69.ThisType<T>
⏱️ 4:11
70.Awaited<T>
⏱️ 4:12
71.String Manipulation Types
⏱️ 3:36
72.Mapped Types as Clauses
⏱️ 4:01
73.Union vs Intersection Mental Model
⏱️ 3:36
74.Enums are Bad
⏱️ 8:11

Enums are Bad

Subscription Required

You must have an active subscription to access this content.
If you already have a subscription, you can sign in.

Issues

Values Change as Members are Added

Before:

enum LoginMode {
email,
social,
}

console.log(LoginMode.email); // 0
console.log(LoginMode.social); // 1

After:

enum LoginMode {
app,
email,
social,
}

console.log(LoginMode.email); // 0 ❌
console.log(LoginMode.social); // 1 ❌

Enums Have Confusing Keys

Confusing for beginners:

enum LoginMode {
app = 0,
email = 1,
social = 2,
}
// Because Enums Offer Lookup and Reverse Lookup
console.log(LoginMode['app']); // 0
console.log(LoginMode[0]); // 'app'

// Get all the keys
const keys = Object.keys(LoginMode);

console.log(keys);
// Want: ['app', 'email', 'social']
// Got: ['app', 'email', 'social', '0', '1', '2']

Simple Alternative

Just use string unions e.g.:

type LoginMode =
| 'app'
| 'email'
| 'social';

function initiateLogin(loginMode: LoginMode) {
// ...
}

initiateLogin('app'); // ✅

Advanced Alternative

Don't recommend creating this, but if you want an object to get an easy handle to all the runtime values, you can create one easily e.g.:

export const LoginMode = {
device: 'device',
email: 'email',
social: 'social',
} as const;

export type LoginMode = keyof typeof LoginMode;

export function initiateLogin(mode: LoginMode) {
// ...
}

initiateLogin('device');

initiateLogin(LoginMode.device);

Object.keys(LoginMode); // ['device', 'email', 'social']
javascript
typescript
react
playwright

Enjoy free content straight from your inbox 💌

No spam, unsubscribe at any time.

Transcript

00:00

Now you might have noticed that we don't have a lesson on TypeScript in NUMs. That is because I don't recommend using them for professional use. So in this lesson we will look at a comprehensive list of all the reasons, uh, why you shouldn't use TypeScript NUMs. And not only that, we will also provide alternatives that you should use. So let's go. Now, the first reason, and this is probably the biggest one, is that they are not a part of standardized JavaScript. Back then when TypeScript was originally introduced, there was a proposal to sort of put them within JavaScript, but that proposal hasn't seen any significant traction.

00:35

TypeScript works best when you think of it, is a type only addition to JavaScript and currently Enums break this rule to understand more reasons why they are harmful. Let's take a look at how they work within TypeScript. Let's create a simple enum for two ways that the user can log in called login mode and the values are email and social. Now at runtime, the values are going to be numeric by default and they'll start off at zero and then increment by one for each new member. You might already see an issue with this. For example, if the next developer comes in

01:08

and adds another login mode called app and does the great job of putting it in alphabetic order, putting it first. Now email and social are no longer zero and one because app is not zero, email is one and social is two. Now this might not sound like a big deal, but if you are sending these values, for example, over a network, congratulations, you've broken your clients. Now the fix for this is not particularly hard. Make sure that if you are using enums always provide an exact value. So when we initialize, we should provide zero

01:41

and one so that that stay a static. And then when app gets added, it must be assigned a different number. I think you can see why this is going to become a problem because you can actually assign two enums the same value, but let's assume that people are going to do the right thing. Another reason why TypeScript ins are bad is that they don't do a great job of the one thing that TypeScript is designed to do, which is provide type safety. So if we have a nice enum with well-defined values, and if you want to use it within a function, we can annotate it to make sure that a login mode is passed in.

02:14

And you would hope that Touch Street will of course make sure that a valid login mode value is passed in. And of course you can pass in members by looking them up from the enum and this is definitely going to work reliably. But you can actually also pass in the numeric values. And of course if you provide a valid one, everything is going to work perfectly fine, but you can actually provide an invalid value as well, and types scraped will not complain. Another nail in the coffin of numeric NUS is a feature that feels like a bug. And this feature is a nice lookup

02:47

and the reverse lookup that you can do within a nu between the string member names and the numeric values. For example, given the string app, you can see that its value is going to be zero. And given the Value zero, you can see that the key for that is going to be app. Now, as you would imagine in order to support this, TypeScript would need to create an object that has both the string as well as the number values as keys, and that is exactly what TypeScript does. The code might look a bit complicated, but in simple terms, it initializes a new object, passes it as an argument to a function, which then takes the object

03:22

and assigns the string members the number. And this equal to number will then evaluate to the number, so it assigns the number member the string. Now even though we've explained the exact J script implementation, you can pretty much deduce it yourself as well because the string gives the number and the number gives the string. Now because of this simple feature, object keys does not return what you might want it to, you would probably want it to return the keys of the interim, which is app, email, and social. But that is not what you're going to get.

03:54

You're going to get the keys as well as the reverse keys. I think we've established that numeric nus aren't great, but string in NUMs aren't great either to create a TypeScript inam that is backed by strings, you simply initialize the members with string values. Now, even at this point, I want to point out that this is not very dry. It doesn't follow, do not repeat yourself because we are repeating ourselves by mentioning the same thing in the keys and the values stringing S do, however, fix some of the issues of numeric S.

04:27

If you try to get the keys, there is no requirement for a reverse lookup. So you do get the things that you would expect, which is the actual keys. And another thing that they improve is that there are stable open network calls because of course you are forced to assign valid strings, and additionally, they are easier to debug as well. But in addition to the fact that you have to repeat yourself, they continue to increase the ity of our code. Now of course, it is perfectly fine that if you want to write a function that will only take a login mode, you would have to import the type

05:00

and then you would have to use it in a type annotation. This is okay. However, if you use a function that requires an enum, it's going to be more verbose than it needs to be. You cannot simply initiate login with a valid member. For example, if you've got the string from a network, you still need to find a way to convert this into an actual num, which you will most likely do with the type assertion. So anytime you have to use sub code, that depends on a num, you have to first bing in the inam and then look up the member from the inam even though it should be able to accept the valid string.

05:36

Now, this is not a big deal with a single inam, however, quite commonly in libraries like React components take a number of props, which if you decide to convert to inam, then all of a sudden you're bringing in dozens of imports just to call a simple component. Now that we've talked about why types kits are bad, let's focus our attention on alternatives that Work well with the compile time type only nature of TypeScript and the runtime nature of JavaScript. The solution is going to be pretty obvious. Use something that is type only. For example, a union of string literals,

06:10

an immediate benefit is that it follows the dry principle, as you would expect, just like NUMs. You get to use it in a type annotation. And what's great is that you can pass in the valid values without having to first convert them into a special type. And this is actually very strongly typed by typescripts and you even get nice auto complete when you have to provide these string values. Now, an additional concern that people have sometimes is that, does it work with refactoring? Well, yes, it actually works very well with refactoring. If you were to refactor a member of the string union,

06:43

anytime that particular union is assigned, TypeScript will natively refactor that string literal for you. Simple unions of string literals is what I use most of the time. Now, some of you might want additional features that are provided by NUMs in other programming languages, like a list of valid values that you can iterate over. And there are neat JavaScript native patterns for that as well that we can add simple annotations to without having to resort to TypeScript NUMs. For example, we can create a simple object where the keys and the values are the same,

07:15

and we can add the special annotation called as consed. That basically enforces that all the values are narrowed down to their literals. So instead of device being a string, it would actually be exactly the literal string device. And then we can infer that literal union that we had before, which is device, email, and social by first looking up the type inferred for this JavaScript object, and then getting the type of the keys for that particular object using the TypeScript key of operator. So login mode would be the same union

07:47

that we previously had, which means that it has all the same benefits. For example, we can use it in a nice type annotation for a function. And of course, this means that we can pass in the literals just like we did before, as long as they are valid. Now the object comes in handy if you want to do a lookup instead of typing in a string. And additionally, it gives you a nice access to the valid values if you want to use them.

Professional TypeScript Masterclass

Professional TypeScript Masterclass

1.Introduction
free
⏱️ 1:54
2.Setup
free
⏱️ 5:44
3.Primitive Types
free
⏱️ 1:42
4.Instance Types
free
⏱️ 1:52
5.Arrays And Tuples
free
⏱️ 1:38
6.Objects
free
⏱️ 1:33
7.const declarations
free
⏱️ 1:03
8.Function Types
free
⏱️ 1:57
9.Structural Typing
free
⏱️ 2:10
10.Classes in TypeScript
free
⏱️ 1:48
11.Target Compiler Option
free
⏱️ 2:37
12.Generics
⏱️ 3:02
13.Special Types any And unknown
⏱️ 2:00
14.JavaScript to TypeScript
⏱️ 1:32
15.Frontend Projects
⏱️ 3:49
16.Type Assertions
⏱️ 2:15
17.Type Casting
⏱️ 1:16
18.Modules
⏱️ 1:55
19.Type Declarations
⏱️ 4:25
20.Creating NPM packages
⏱️ 3:20
21.Async Await
⏱️ 3:05
22.Running in NodeJS
⏱️ 1:40
23.Lexical this
⏱️ 2:34
24.readonly Modifier
⏱️ 1:59
25.Union Types
⏱️ 2:57
26.Literal Types
⏱️ 2:58
27.Type Narrowing
⏱️ 4:19
28.Discriminated Unions
⏱️ 3:29
29.Class Parameter Properties
⏱️ 1:02
30.Strict Compiler Option
⏱️ 6:18
31.null vs undefined
⏱️ 4:19
32.Intersection Types
⏱️ 2:03
33.Optional Modifier
⏱️ 2:47
34.Non Null Assertion Operator
⏱️ 3:40
35.Interfaces
⏱️ 2:28
36.Interface Declaration Merging
⏱️ 1:01
37.Types vs Interfaces
⏱️ 2:16
38.never Type
⏱️ 3:00
39.implements Keyword
⏱️ 1:25
40.Definite Assignment Assertion
⏱️ 2:31
41.User Defined Type Guards
⏱️ 2:02
42.Assertion Functions
⏱️ 3:42
43.Function Overloading
⏱️ 4:15
44.Call Signatures
⏱️ 2:53
45.Abstract Classes
⏱️ 1:53
46.Index Signatures
⏱️ 3:08
47.Readonly Arrays and Tuples
⏱️ 2:58
48.Double Assertions
⏱️ 2:20
49.const Assertions
⏱️ 3:55
50.this Parameter
⏱️ 2:33
51.Generic Constraints
⏱️ 2:43
52.typeof Type Operator
⏱️ 2:12
53.Lookup Types
⏱️ 3:12
54.keyof Type Operator
⏱️ 3:55
55.Conditional Types
⏱️ 4:39
56.Contitional Types with Unions and never
⏱️ 3:32
57.infer Keyword and `ReturnType<T>`
⏱️ 3:47
58.Mapped Types
⏱️ 2:48
59.Mapped Type Modifiers
⏱️ 3:37
60.Template Literal Type
⏱️ 4:28
61.Partial<T>
⏱️ 1:27
62.Required<T>
⏱️ 1:36
63.Readonly<T>
⏱️ 1:34
64.Record<K, T>
⏱️ 4:05
65.Project References
⏱️ 4:18
66.undefined vs. optional
⏱️ 2:48
67.satisfies Operator
⏱️ 2:42
68.PropertyKey Type
⏱️ 0:57
69.ThisType<T>
⏱️ 4:11
70.Awaited<T>
⏱️ 4:12
71.String Manipulation Types
⏱️ 3:36
72.Mapped Types as Clauses
⏱️ 4:01
73.Union vs Intersection Mental Model
⏱️ 3:36
74.Enums are Bad
⏱️ 8:11