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
95: String Enums vs Numeric Enums
By now, you've seen that enums are great for keeping a set of related constants in one place. But when you actually start typing them out, you'll notice TypeScript gives you a choice: you can let the compiler handle the values (numeric) or you can define them yourself (string). Let's clear up which one you should actually be using.
If I don't assign values to my enum, what's actually happening?
If you leave the values blank, TypeScript creates a numeric enum. It starts at 0 and increments by one for every member. It feels convenient because it's less typing, but it's often a trap for beginners.
enum OrderStatus {
Pending, // 0
Shipped, // 1
Delivered, // 2
Cancelled // 3
}
const currentStatus = OrderStatus.Pending;
console.log(currentStatus); // Output: 0
Here's the problem: if you later decide to insert a new status, like Processing, between Pending and Shipped, all the numbers after it shift. If you've already saved these numbers in a database, you've just corrupted your data. I've seen this happen in production more times than I'd like to admit.
Why should I bother writing out the strings?
String enums require you to initialize every member. It's more verbose, sure, but the payoff is huge when it comes to debugging and API integration.
enum OrderStatus {
Pending = "PENDING",
Shipped = "SHIPPED",
Delivered = "DELIVERED",
Cancelled = "CANCELLED"
}
const currentStatus = OrderStatus.Pending;
console.log(currentStatus); // Output: "PENDING"
Imagine you're staring at a log file at 2:00 AM. Seeing OrderStatus: 2 tells you absolutely nothing unless you have the source code open in another window. Seeing OrderStatus: "DELIVERED" tells you exactly what's happening. Plus, since the value is explicitly defined, you can reorder the enum members or add new ones without breaking any existing logic or database records.
I've heard something about "reverse mapping"—does that work for both?
This is one of those TypeScript quirks that confuses people. Numeric enums support reverse mapping, meaning you can go from the value back to the name.
enum OrderStatus {
Pending, // 0
Shipped // 1
}
console.log(OrderStatus.Pending); // 0
console.log(OrderStatus[0]); // "Pending"
String enums do not support this. If you try OrderStatus["PENDING"] on a string enum, it won't give you the key name back. In my experience, you almost never actually need reverse mapping in a real-world app, so this isn't usually a reason to choose numeric enums over string ones.
Which one should I actually use in a real project?
If you're undecided, go with string enums. 95% of the time, they are the right choice because they are self-documenting and safer for data persistence. Use numeric enums only if the actual integer value is meaningful to your logic (like mapping to specific HTTP status codes or bitwise flags) or if you have a very strict memory constraint where storing a small integer is critical.
📋 Practical Task
Refactoring the Legacy Order Tracker
You've inherited a project where the previous developer used a numeric enum for shipment tracking. The app is currently crashing because a new status was added in the middle of the enum, shifting the IDs and causing the frontend to misinterpret "Processing" orders as "Shipped".
Your task:
- Locate the
ShipmentStatusnumeric enum. - Convert it into a string enum so that the values are explicit and won't shift when new members are added.
- Ensure the values match these strings:
"Warehouse","InTransit","OutForDelivery", and"Arrived". - Update the
logStatusfunction to handle the new string values.
// FIX THIS CODE
enum ShipmentStatus {
Warehouse,
InTransit,
OutForDelivery,
Arrived
}
function logStatus(status: ShipmentStatus) {
console.log(`The package is currently: ${status}`);
}
logStatus(ShipmentStatus.InTransit);There are no comments for now.