You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
**This article was last updated on January 6, 2025, to include sections on Common Mistakes with TypeScript Enums and Tips for Using Enums in TypeScript Classes.**
12
+
11
13
## Introduction
12
14
15
+
### TL;DR;
16
+
17
+
:::info FAQ
18
+
19
+
1. What is an enum in TypeScript?
20
+
21
+
- Enumeration or enum is a feature in TypeScript, which allows a set of named constants. This makes the code more readable and maintainable.
22
+
23
+
2. What kinds of enums are there in TypeScript?
24
+
25
+
- TypeScript supports two kinds of enums: string enums, such as enum Color { RED = "Red" }; and numeric enums, such as enum Day { MONDAY = 0 };.
26
+
27
+
3. How do I initialize enum members in TypeScript?
28
+
29
+
- String enums must be initialized explicitly; numeric enums can be uninitialized and will auto-increment starting from 0.
30
+
31
+
4. What are the differences between constant and computed values in enums?
32
+
33
+
- Constant means values are fixed, such as "Red", 0; computed means values are derived from expressions, such as "A".length.
34
+
35
+
5. How do enums behave at runtime?
36
+
37
+
- Enums create the JavaScript object versions at runtime; this therefore includes the ability for bi-directional mapping when these are numeric enums but still only unidirectional mapping exists for string enums
38
+
:::
39
+
13
40
**Enum**s are constants based data structures that store a set of named constants grouped around a central theme or intent. In TypeScript, Enums are a feature that injects runtime JavaScript objects to an application in addition to providing usual type-level extensions.
14
41
15
42
This post explores **enums** in TypeScript with examples from a tiers based Subscription model where subscription entities differ according to account types and billing schedules.
@@ -27,6 +54,8 @@ Steps well'll cover:
27
54
-[Enum Member Values in TypeScript: Constant vs Computed](#enum-member-values-in-typescript-constant-vs-computed)
28
55
-[Types from TypeScript Enums](#types-from-typescript-enums)
29
56
-[Using TypeScript Enums in Classes](#using-typescript-enums-in-classes)
57
+
-[Practical Applications of TypeScript Enums](#practical-applications-of-typescript-enums)
58
+
-[Common Pitfalls Using Enums](#common-pitfalls-using-enums)
30
59
31
60
## Prerequisites
32
61
@@ -103,7 +132,6 @@ Similarly, when all members have numerical values, the enum itself becomes numer
103
132
104
133
```tsx
105
134
enumBillingSchedule {
106
-
// highlight-next-line
107
135
FREE=0,
108
136
MONTHLY,
109
137
QUARTERLY,
@@ -176,7 +204,6 @@ In our `BillingSchedule` enum, we explicitly assigned `0` to the first member:
176
204
// First member initialized, subsequent members auto-increment
177
205
178
206
enumBillingSchedule {
179
-
// highlight-next-line
180
207
FREE=0,
181
208
MONTHLY,
182
209
QUARTERLY,
@@ -198,8 +225,6 @@ As we can see, initializing a member with a number represents an offset value ba
198
225
// No initialization
199
226
200
227
enumBillingSchedule {
201
-
// highlight-next-line
202
-
203
228
FREE,
204
229
MONTHLY,
205
230
QUARTERLY,
@@ -387,14 +412,12 @@ Individual types are generated from each member when all members of the enum are
387
412
388
413
```tsx
389
414
typeTPersonalAccount= {
390
-
// highlight-next-line
391
415
tier:AccountType.PERSONAL;
392
416
postsQuota:number;
393
417
verified:boolean;
394
418
};
395
419
396
420
interfaceIStartupAccount {
397
-
// highlight-next-line
398
421
tier:AccountType.STARTUP;
399
422
postsQuota:number;
400
423
verified:boolean;
@@ -504,12 +527,141 @@ class PersonalSubscription
504
527
505
528
In the above code, for `BillingSchedule` we have used a numeric enum with all uninitialized members. The first member is therefore assigned `0` and subsequent ones get auto-incremented by `1`. We have used generics to pass in `AccountType` and `BillingSchedule` types to `TAccount` and `IBilling` respectively so their use becomes more flexible in the `PersonalAccount` and `FreeBilling` classes as well as in the `IPersonalSubscription` type, where we are using enum members both as constant values as well as type definitions.
Enums are super useful when you have to organize a set of related constants. Here are some real-world examples:
533
+
534
+
### 1. Role-Based Access Control
535
+
536
+
Let's say you're building an app with different user roles. You could use enums to define those clearly:
537
+
538
+
```typescript
539
+
enumUserRole {
540
+
ADMIN="Admin",
541
+
EDITOR="Editor",
542
+
VIEWER="Viewer",
543
+
}
544
+
545
+
function checkAccess(role:UserRole):void {
546
+
if (role===UserRole.ADMIN) {
547
+
console.log("You have full access!");
548
+
} else {
549
+
console.log("Limited access only.");
550
+
}
551
+
}
552
+
```
553
+
554
+
### 2. HTTP Methods for APIs
555
+
556
+
You can use enums to standardize HTTP methods when making API calls:
557
+
558
+
```typescript
559
+
enumHttpMethod {
560
+
GET="GET",
561
+
POST="POST",
562
+
PUT="PUT",
563
+
DELETE="DELETE",
564
+
}
565
+
566
+
function makeRequest(method:HttpMethod, url:string):void {
567
+
console.log(`Sending a ${method} request to ${url}`);
568
+
}
569
+
570
+
makeRequest(HttpMethod.POST, "/api/users");
571
+
```
572
+
573
+
### 3. Error Types
574
+
575
+
Enums are much cleaner for handling errors. You can define error categories like so:
576
+
577
+
```typescript
578
+
enumErrorType {
579
+
NETWORK="Network Error",
580
+
VALIDATION="Validation Error",
581
+
SERVER="Server Error",
582
+
}
583
+
584
+
function logError(type:ErrorType):void {
585
+
console.log(`An error occurred: ${type}`);
586
+
}
587
+
588
+
logError(ErrorType.NETWORK);
589
+
```
590
+
591
+
Enums will make your code more readable and save you from using invalid values. No longer magic strings or numbers everywhere!
592
+
593
+
## Common Pitfalls Using Enums
594
+
595
+
Here are some mistakes I've run into when working with enums—and how I avoid them now:
596
+
597
+
### Forgetting to Initialize String Enums
598
+
599
+
If you are using string enums, then all members must have an initial value. Man, I forgot this one all the time! Nowadays, I initialize them upfront.
600
+
601
+
```typescript
602
+
// Wrong
603
+
enumColor {
604
+
RED,
605
+
GREEN, // Error: Must have an initializer
606
+
}
607
+
608
+
// Correct
609
+
enumColor {
610
+
RED="Red
611
+
GREEN="Green,
612
+
}
613
+
```
614
+
615
+
### Hardcoded Values
616
+
617
+
Early on, I would hard-code values like "Admin" or 0, which became a nightmare to maintain. Nowadays, I always use enums for cleaner, centralized constants.
618
+
619
+
```typescript
620
+
// Instead of this:
621
+
const userRole ="Admin";
622
+
623
+
// Use this:
624
+
enumUserRole { ADMIN="Admin,
625
+
626
+
EDITOR="Editor,
627
+
628
+
629
+
}
630
+
631
+
const userRole =UserRole.ADMIN;
632
+
```
633
+
634
+
### Confusing Enum Keys and Values
635
+
636
+
I’ve definitely tried to use a value when a key was needed or vice versa. With numeric enums, you can go both ways, but string enums are more strict.
637
+
638
+
```typescript
639
+
enumStatus {
640
+
ACTIVE="Active",
641
+
INACTIVE="Inactive",
642
+
}
643
+
644
+
console.log(Status.ACTIVE); // "Active"
645
+
console.log(Status["Active"]); // Error: Property 'Active' does not exist
646
+
```
647
+
648
+
### No Logging for Errors
649
+
650
+
Cron jobs used to fail silently until I started logging errors. The same principle applies to enums—you should always validate that the values you pass are correct, especially when enums interact with APIs or third-party systems.
651
+
652
+
### Overcomplicating Numeric Enums
653
+
654
+
I once manually assigned values to every member in a numeric enum. Turns out, you can let TypeScript auto-increment them for you:
655
+
656
+
```typescript
657
+
enumBillingCycle {
658
+
FREE=0,
659
+
MONTHLY, // 1
660
+
YEARLY, // 2
661
+
}
662
+
```
663
+
664
+
By avoiding these mistakes, my code has become more reliable and easier to debug! Enums are great when used well.
**This article was last updated on January 6, 2025, to include sections on Common Mistakes with TypeScript Record and Tips for Using Record in TypeScript.**
12
+
13
+
## What is Record Type in TypeScript?
14
+
11
15
The `Record<>` utility type in TypeScript is typically associated with a record or a collection of records returned from an API endpoint. It helps define a type with property names such as `id` and map the values to the type of the data.
12
16
13
17
## Introduction
@@ -16,6 +20,33 @@ The `Record<>` type is a TypeScript object transformation type that is often use
16
20
17
21
This post explores the TypeScript Record type with a series of examples ranging from simple strings and numbers based types to more common ones involving API data and React components. With the examples, we see how to derive `Record<>` types by assigning types for `Keys` and `Value`, discuss how a `Record<>` type is advantageous over simple object types and index signatures, and try to understand the quirks of its usage.
18
22
23
+
:::tip FAQs about TypeScript Record Type
24
+
25
+
- Q: What is the `Record<>` type in TypeScript?
26
+
The `Record<>` type is useful in specifying an object whose keys and values have explicit types. Ensure type safety for dynamic objects.
27
+
28
+
- Can the `Record<>` type have keys other than `string`?
29
+
Yes, for the keys we can use `string`, `number`, or `symbol`. All other types are prohibited for the keys.
30
+
31
+
- How does `Record<>` differ from the index signature?
32
+
`Record<>` provides stricter type checking, while index signatures (`[key: string]: Value`) are more flexible but less type-safe.
33
+
34
+
- Can I use the `Record<>` type with React components?
35
+
Yes, `Record<>` does work with JSX components as values. You can map component names or props to components.
36
+
37
+
- How do I constrain keys in a `Record<>` type?
38
+
39
+
You can define a union of allowed keys for the `Keys` type.
40
+
41
+
For example:
42
+
43
+
```ts
44
+
typePermissions="Admin"|"User"|"Guest";
45
+
typePermissionMap=Record<Permissions, string>;
46
+
```
47
+
48
+
:::
49
+
19
50
## Understanding the Record Type
20
51
21
52
Starting easy, let's begin with a simple **object type** that represents a user:
- It is easy for developers to think that `Record<>` enforces key and value constraints together, which is just not true. Remember `Keys` applies only to the names of the properties.
304
+
305
+
```typescript
306
+
typeExample=Record<string, number>;
307
+
const data:Example= { key: "value" }; // Error: "value" is not a number
308
+
```
309
+
310
+
### Over-Complicating the Type
311
+
312
+
- If it's something you can dynamically generate with enums, or mapped types then don't manually define every key in a union.
313
+
314
+
### Best Practices
315
+
316
+
- Use `Record<>` when: - You need type-safe objects with dynamic keys.
317
+
- You must map keys to complex types, like objects, components or unions.
318
+
- You need a lightweight alternative to creating interfaces or types for objects with simple mappings.
319
+
- Use unions to constrain keys instead of an unnecessary flexibility being given.
320
+
- Validate API responses when using `Record<>` to map backend data.
321
+
260
322
## Summary
261
323
262
324
In this post we explored how to use the `Record<>` type in TypeScript to construct stable types that are error-prone and more maintainable. We saw how the derived type is a hash map based on a type that represents the actual shape of the data. It also accepts and assigns types to member keys of the map, which can be restricted by using a union type. We have seen an example of using `Record<>` to type `users` data for an API endpoint as well as one example that uses `Record<>` type for rendering React components.
0 commit comments