Understanding Enums in TypeScript
SkillVeris Team
Engineering Team

A TypeScript enum is a named set of related constants that lets you refer to fixed values by meaningful names instead of raw numbers or strings.
In this guide, you'll learn:
- Numeric enums auto-increment from zero by default, while string enums assign an explicit readable value to each member.
- Enums generate real JavaScript objects at runtime, so they add code to your bundle unlike pure type constructs.
- const enums are inlined at compile time and leave no runtime object, trading flexibility for smaller output.
- Union types of string literals often replace enums with less overhead and better narrowing.
1What Is an Enum in TypeScript?
An enum in TypeScript is a way to give a group of related constant values a set of friendly names. Instead of scattering magic numbers or bare strings through your code, you define them once — like Direction.Up or Status.Active — and refer to them by name everywhere.
Enums make code more readable and self-documenting. A function that accepts Status.Pending communicates intent far more clearly than one that accepts the number 2, and the compiler stops you from passing a value that is not part of the set.
2Numeric Enums
Numeric enums are the default. Each member gets a number, starting at zero and auto-incrementing unless you assign a value explicitly. You can set a starting point and let the rest follow.
- enum Direction { Up, Down, Left, Right } // 0, 1, 2, 3
- enum StatusCode { OK = 200, NotFound = 404, Error = 500 }
- let move: Direction = Direction.Up
- enum Level { Low = 1, Medium, High } // 1, 2, 3
⚠️Reverse Mapping Surprise
Numeric enums generate a reverse map, so Direction[0] returns the string 'Up'. This is occasionally useful but can leak unexpected keys if you iterate over the enum object.
3String Enums
String enums assign an explicit string to each member. They lack the auto-increment convenience, but the values are far more debuggable: a logged Status.Active reads as 'ACTIVE' rather than a cryptic 2.
String enums do not create a reverse mapping, which keeps the generated object cleaner. For anything that crosses a network boundary or ends up in logs, string enums are almost always the better choice.
- enum Status { Active = 'ACTIVE', Inactive = 'INACTIVE', Banned = 'BANNED' }
- enum LogLevel { Debug = 'debug', Info = 'info', Error = 'error' }
- const current: Status = Status.Active // 'ACTIVE' at runtime
4const Enums
A const enum is inlined by the compiler. Where you write Direction.Up, TypeScript substitutes the literal value directly and emits no enum object at all, producing smaller output for hot paths or size-sensitive code.
The trade-off is flexibility: because there is no runtime object, you cannot iterate over a const enum or use its reverse mapping. Some build setups with isolated module compilation also restrict const enums, so check your tooling before relying on them.
- const enum Direction { Up, Down, Left, Right }
- let d = Direction.Up // compiles to: let d = 0
- // no Direction object exists at runtime
5Enums vs Union Types
A union of string literals often does the same job as a string enum with less machinery. It exists purely in the type system, adds no runtime code, and narrows cleanly in switch statements.
- type Status = 'active' | 'inactive' | 'banned'
- function setStatus(s: Status) { }
- setStatus('active') // valid, no import needed
- // vs enum: setStatus(Status.Active)
When Each Wins
Literal unions are lighter and let callers pass a plain string, which is convenient for config and API payloads. Enums are better when you want a single named namespace to import, or when you genuinely need the runtime object to iterate over the members.
6How Enums Behave at Runtime
Unlike most TypeScript features, a regular enum is not erased during compilation. It becomes a real JavaScript object, which means it occupies space in your bundle and can be inspected, iterated, and passed around at runtime.
This dual nature — a type and a value at the same time — is what separates enums from interfaces and type aliases. Being aware of it helps you predict the generated code and avoid surprises when tree-shaking or measuring bundle size.
🔑Type and Value
An enum name is usable in both type position and value position. That flexibility is powerful, but it also means enums ship JavaScript while type aliases and unions do not.
7Practical Patterns
A few patterns keep enums predictable and pleasant to work with in real projects.
- Prefer string enums over numeric ones for anything that gets serialized or logged.
- Use enum values as object keys with Record to build exhaustive lookups.
- Add a default or exhaustive check in switch statements so a new member forces you to handle it.
- Keep related enums close to the types that use them for discoverability.
8Common Mistakes to Avoid
Enums trip up newcomers in a handful of predictable ways.
- Relying on numeric enum values across a network boundary, then breaking clients when you reorder members and the numbers shift.
- Iterating a numeric enum and being confused by the reverse-mapping keys mixed in with the real members.
- Reaching for const enums without checking that your build tool supports them under isolated modules.
- Using a heavyweight enum when a simple literal union would carry zero runtime cost.
- Assuming an enum is fully type-erased and being surprised it appears in the compiled bundle.
9Key Takeaways
The right enum choice depends on how the values are used and shipped.
- Numeric enums auto-increment and create a reverse map; good for internal ordinal values.
- String enums use explicit readable values and are safest across boundaries.
- const enums inline at compile time for smaller output but lose runtime flexibility.
- Literal unions replace many enums with no runtime footprint.
- Pick enums for a named value namespace, unions for the lightest type.
10Frequently Asked Questions
Q: Do TypeScript enums exist at runtime? A: Regular enums do — they compile into a JavaScript object that lives in your bundle. const enums are the exception; they are inlined at compile time and leave no runtime object behind.
Q: Should I use enums or union types? A: Use a literal union when you want the lightest type with no runtime cost and are happy passing plain strings. Use an enum when you want a single named namespace to import or need to iterate over members at runtime.
Q: Why are string enums recommended over numeric ones? A: String enum values are self-describing in logs and payloads, and they do not shift when you reorder members. Numeric values are positional, so adding or moving a member can silently change every downstream number.
Q: Can I loop over an enum's members? A: Yes for regular enums, using Object.values or Object.keys, though numeric enums also expose reverse-mapping keys you may need to filter. const enums cannot be iterated because they have no runtime object.
Related Reading
Get The Print Version
Download a PDF of this article for offline reading.
About the Publisher
SkillVeris Team
Engineering Team
Our engineering writers turn abstract code concepts into hands-on, project-driven learning experiences.
View all postsRelated Posts
Never miss an update
Get the latest tutorials and guides delivered to your inbox.
No spam. Unsubscribe anytime.