TypeScript
Completed
-
Section 1: Getting Started
-
Section 2: Basic Types
-
Section 3: Functions and Objects
-
Section 4: Advanced Types
-
Section 5: Object-Oriented TypeScript
-
Section 6: Working with Modules
-
Section 7: Tooling and Practice
-
Section 8: Type-Level Programming
-
Section 9: TypeScript with Backends
-
Section 10: Testing Typed Code
-
Section 11: Data Validation with Types
-
Section 12: Practical Projects
-
Section 13: Compiler Internals
-
Section 14: Configuration Deep Dive
-
Section 15: Enums, Symbols, and Special Types
-
Section 16: Working with Async Code
-
Section 17: TypeScript and the DOM
-
Section 18: Advanced Generics Practice
-
Section 19: Working with Third-Party Types
-
Section 20: Monorepo and Large-Scale Practices
-
Section 21: Common Pitfalls and Best Practices
-
Section 22: Interview Practice
-
Section 23: Handbook: Narrowing In Depth
-
Section 24: Handbook: Object Types In Depth
-
Section 25: Handbook: Classes In Depth
-
Section 26: Handbook: Modules In Depth
-
Section 27: Handbook: Declaration Files In Depth
-
Section 28: JSX and Namespaces
-
Section 29: Compiler Configuration Reference
-
Section 30: More Practice Exercises
-
Section 31: Handbook: Everyday Types Deep Dive
-
Section 32: Utility Types Full Reference
-
Section 33: Decorators Reference
-
Section 34: Mixins and Advanced OOP Patterns
-
Section 35: Iterators and Generators Typing
-
Section 36: More Type-Level Programming Practice
-
Section 37: TypeScript Ecosystem Tools
-
Section 38: TypeScript with Testing Frameworks
-
Section 39: TypeScript for Library Authors
-
Section 40: More Interview Practice
-
Section 41: Handbook: Functions In Depth
-
Section 42: Handbook: Type Manipulation Deep Dive
-
Section 43: More Real-World Patterns
-
Section 44: TypeScript Release Notes Highlights
-
Section 45: Final Practice and Review
9: Enums and Const Enums
Imagine you're running a busy coffee shop. You could tell your baristas, "When a customer orders a '1', make a Latte; a '2' is a Cappuccino, and a '3' is a Flat White." Technically, that works. But it's a nightmare. One morning, a new hire forgets if '2' was a Cappuccino or a Mocha, or someone accidentally types '4' into the system, and suddenly you're serving something that doesn't exist. To fix this, you create a menu. The menu gives a human-readable name (the "Label") to a specific item (the "Value").
That is exactly what an Enum does in TypeScript. It lets you define a set of named constants so you aren't passing "magic numbers" or random strings around your codebase and hoping you didn't make a typo.
Giving Names to Magic Numbers
Let's say we're building an e-commerce order tracker. Without enums, you might use numbers to represent the status of an order. But if (order.status === 2) tells you absolutely nothing about what's actually happening. Here is how I'd handle that using a numeric enum:
enum OrderStatus {
Pending, // 0
Processing, // 1
Shipped, // 2
Delivered, // 3
}
function updateShipping(status: OrderStatus) {
if (status === OrderStatus.Shipped) {
console.log("Notify the customer their package is on the way!");
}
}
updateShipping(OrderStatus.Shipped);
By default, TypeScript assigns numbers starting at 0. The beauty here is that OrderStatus.Shipped is much easier to read than the number 2. Also, if you ever need to change the underlying value, you change it in one place, not in fifty different if statements across your app.
When Strings Make More Sense
Numeric enums are great, but they have a downside: when you log them to the console or send them to an API, you just see 0, 1, 2. If you're debugging a production log at 2 AM, you don't want to be cross-referencing a documentation file to remember what "Status 2" means. That's where string enums come in.
enum OrderStatus {
Pending = "PENDING",
Processing = "PROCESSING",
Shipped = "SHIPPED",
Delivered = "DELIVERED",
}
const currentStatus = OrderStatus.Shipped;
console.log(currentStatus); // Output: "SHIPPED"
I almost always prefer string enums for things that get saved to a database or sent over a network. It makes the data self-documenting. You don't need a secret decoder ring to understand the state of your application.
Stripping the Runtime Overhead
Now, here is a bit of a "pro" detail. Standard enums are one of the few features in TypeScript that aren't just type-level sugar—they actually generate a real JavaScript object when the code is compiled. For most projects, this doesn't matter. But if you're writing a library or working in a high-performance environment, you might not want that extra object hanging around in your final bundle.
That's why we have const enum. When you add the const keyword, TypeScript doesn't create an object at runtime. Instead, it simply replaces every mention of the enum member with the actual value during compilation.
const enum UserRole {
Admin = 1,
Editor = 2,
Viewer = 3,
}
const myRole = UserRole.Admin;
// In the compiled JavaScript, this literally becomes:
// const myRole = 1;
You lose the ability to iterate over the enum keys at runtime, but you gain a tiny bit of performance and a smaller file size. Use them when you know you only need the names for type-checking and don't need to treat the enum as an object in your JS code.
📋 Practical Task
Building a User Access Level Controller
You are building a permission system for a corporate dashboard. You need to ensure that users are assigned a specific access level and that the system handles these levels safely.
Your Task:
- Create a
string enumcalledAccessLevelwith the following members:Guest,Employee,Manager, andAdmin. - Write a function called
canAccessAdminPanelthat takes anAccessLevelas an argument. - The function should return
trueonly if the user is anAdminor aManager; otherwise, returnfalse. - Test your function by calling it with both
AccessLevel.GuestandAccessLevel.Adminand logging the results to the console.
There are no comments for now.