How to Iterate Over Enums in TypeScript: A Complete Guide

Master every iteration technique for TypeScript enums--from Object.keys() to type-safe patterns--with practical examples for Next.js and React projects.

TypeScript enums provide a powerful way to define named constants, but iterating over them requires understanding their unique behavior. Unlike arrays, enums compile to different JavaScript structures depending on their type, which affects how you can iterate through their keys and values.

This comprehensive guide covers every iteration method, from basic approaches to advanced patterns, with practical examples for modern web development projects built with Next.js and React.

What you'll learn:

  • The three types of TypeScript enums and their runtime behavior
  • Multiple iteration methods including Object.keys(), Object.values(), and for...in loops
  • Type-safe patterns for reliable enum iteration
  • Performance considerations and best practices
  • Common pitfalls and how to avoid them

Understanding enum iteration is essential when building type-safe applications that rely on constant values for configuration, status tracking, or configuration management.

Understanding TypeScript Enum Types

Before diving into iteration techniques, it's essential to understand the three types of TypeScript enums and how they behave at runtime. Each type compiles differently, which directly impacts your iteration strategy.

Numeric Enums

Numeric enums are the most common type and auto-increment by default. When you declare an enum without explicit values, TypeScript assigns sequential numbers starting from 0.

enum Direction {
 North, // 0
 East, // 1
 South, // 2
 West // 3
}

Numeric enums produce both a forward mapping (name to value) and a reverse mapping (value to name) at runtime. This dual-mapping behavior creates interesting iteration possibilities but also introduces overhead.

String Enums

String enums provide more semantic value but behave differently at runtime. They don't have reverse mappings, which affects iteration performance and approach.

enum Status {
 Pending = "PENDING",
 Active = "ACTIVE",
 Completed = "COMPLETED"
}

Const Enums

Const enums are completely inlined at compile time, meaning they don't exist as objects at runtime. This has significant implications for iteration--you cannot iterate over const enums using standard object methods because they disappear after compilation.

const enum Priority {
 Low = "LOW",
 Medium = "MEDIUM",
 High = "HIGH"
}

Key insight: Only regular (non-const) enums can be iterated at runtime. Use const enums when you need performance for direct access but avoid them when iteration is required.

When to Use Each Enum Type

TypeUse WhenIteration SupportPerformance
NumericRelated numeric values, flagsFull supportModerate
StringMeaningful string values, API responsesFull supportGood
ConstPerformance-critical, no iteration neededNoneExcellent

Methods for Iterating Over Enums

TypeScript provides several approaches for iterating over enums, each with distinct characteristics suited to different scenarios.

Using Object.keys() for Key Iteration

The Object.keys() method returns an array of the enum's keys, which correspond to the enum member names. This approach works reliably for both numeric and string enums.

enum Color {
 Red = "RED",
 Green = "GREEN",
 Blue = "BLUE"
}

const colorKeys = Object.keys(Color);
// ["Red", "Green", "Blue"]

For numeric enums, Object.keys() returns only the member names, not the numeric values. You'll need additional processing to access both keys and values.

Using Object.values() for Value Iteration

Object.values() returns an array of the enum's values, providing direct access to the underlying data.

enum HTTPStatus {
 OK = 200,
 Created = 201,
 Accepted = 202
}

const statusValues = Object.values(HTTPStatus);
// [200, 201, 202, "OK", "Created", "Accepted"]

Important: For numeric enums, Object.values() returns both the numeric values AND the string names due to the reverse mapping. This dual return requires filtering if you want only one type of value.

Using for...in Loops

The for...in statement iterates over all enumerable string properties of an object, making it suitable for enum iteration with more control over the process.

enum Transport {
 Car = "CAR",
 Train = "TRAIN",
 Plane = "PLANE"
}

for (const key in Transport) {
 console.log(`${key}: ${Transport[key]}`);
}
// Car: CAR
// Train: TRAIN
// Plane: PLANE

for...in provides access to both keys and values during iteration, which can be useful when you need to perform operations using both.

Using for...of with Object.entries()

Combining for...of with Object.entries() provides a clean, readable approach to enum iteration with full access to both keys and values.

enum Season {
 Spring = "SPRING",
 Summer = "SUMMER",
 Autumn = "AUTUMN",
 Winter = "WINTER"
}

for (const [key, value] of Object.entries(Season)) {
 console.log(`${key} => ${value}`);
}
// Spring => SPRING
// Summer => SUMMER
// Autumn => AUTUMN
// Winter => WINTER
Iteration Methods at a Glance

Choose the right approach for your use case

Object.keys()

Returns enum member names as an array. Simple and predictable for all enum types.

Object.values()

Returns all values including reverse mappings. Requires filtering for numeric enums.

for...in

Iterates with direct access to keys and values. Good for complex iteration logic.

Object.entries() + for...of

Clean syntax with destructuring. Most readable for modern TypeScript codebases.

Type-Safe Enum Iteration

TypeScript's type system can help ensure safe enum iteration, preventing runtime errors and improving code reliability. When building scalable web applications, proper type safety becomes essential for maintainability.

Using Type Guards

Type guards help filter out unwanted entries when iterating, particularly useful for numeric enums where reverse mappings appear alongside regular members.

enum LogLevel {
 Debug = 0,
 Info = 1,
 Warn = 2,
 Error = 3
}

function isLogLevelKey(key: string): key is keyof typeof LogLevel {
 return key in LogLevel;
}

Object.keys(LogLevel).filter(isLogLevelKey);
// ["Debug", "Info", "Warn", "Error"]

Excluding Reverse Mappings

Numeric enums create reverse mappings that appear as additional properties. Several techniques help exclude these from iteration results.

enum Priority {
 Low = 1,
 Medium = 2,
 High = 3
}

// Filter approach: exclude numeric keys
const enumKeys = Object.keys(Priority).filter(
 key => isNaN(Number(key))
);
// ["Low", "Medium", "High"]

// Alternative: use type assertions
const directKeys = Object.keys(Priority) as (keyof typeof Priority)[];

Reusable Utility Functions

Create reusable utilities for type-safe enum iteration across your codebase. These patterns are especially valuable in large-scale TypeScript projects where consistency matters.

function getEnumKeys<T extends object>(enumObj: T): (keyof T)[] {
 return Object.keys(enumObj).filter(
 key => isNaN(Number(key))
 ) as (keyof T)[];
}

function getEnumValues<T extends object>(enumObj: T): T[keyof T][] {
 return Object.values(enumObj).filter(
 value => typeof value === "string" || typeof value === "number"
 ) as T[keyof T][];
}

For more TypeScript patterns and techniques, explore our comprehensive guides on advanced type manipulation.

Performance Considerations

Understanding performance characteristics helps choose the right iteration method for your specific use case.

Runtime Object Creation

Object.keys() and Object.values() create new arrays on each call, which can impact performance in tight loops or large enums. For frequently iterated enums, consider caching the results.

// Cache iteration results for frequently-used enums
const cachedColorKeys = Object.keys(Color);
const cachedColorValues = Object.values(Color);

for...in vs. Object Methods

for...in loops are generally more memory-efficient for large enums since they don't create intermediate arrays, though the difference is negligible for typical use cases with fewer than 100 enum members.

Const Enum Trade-offs

While const enums eliminate runtime overhead entirely, this benefit comes at the cost of iteration capability. Use const enums when you need performance for direct access but avoid them when iteration is required.

// Good: const enum for direct access
const enum Config {
 MaxRetries = 5
}
const retries = Config.MaxRetries; // Inlined at compile time

// Bad: const enum when iteration is needed
const enum Status {
 Pending = "PENDING"
}
// Object.keys(Status) will not work as expected!

Performance Comparison Table

MethodTime ComplexitySpace ComplexityBest For
Object.keys()O(n)O(n)Simple key iteration
Object.values()O(n)O(n)Value collection
for...inO(n)O(1)Memory-constrained scenarios
Object.entries() + for...ofO(n)O(n)Key-value pairs

Where n is the number of enum members

Common Patterns and Best Practices

Pattern: Enum Member Metadata

For complex scenarios, consider storing additional metadata with your enums.

enum APIEndpoint {
 Users = "/api/users",
 Posts = "/api/posts"
}

const endpointMetadata: Record<keyof typeof APIEndpoint, string> = {
 Users: "User management endpoints",
 Posts: "Blog post endpoints"
};

Pattern: Dynamic Enum Creation

For scenarios where enum values come from configuration or API responses, consider using TypeScript's mapped types instead of traditional enums.

// Instead of traditional enum when values are dynamic
type Status = {
 pending: "pending";
 active: "active";
 completed: "completed";
};

const Status = {
 pending: "pending" as const,
 active: "active" as const,
 completed: "completed" as const
};

// Still iterable with Object.keys()
Object.keys(Status); // ["pending", "active", "completed"]

Best Practices Summary

  1. Use type guards when filtering enum members to ensure type safety
  2. Cache results for frequently iterated enums to improve performance
  3. Prefer regular enums over const enums when iteration is required
  4. Document enum behavior especially when using reverse mappings
  5. Consider mapped types for dynamic or configurable enum-like structures

Related Resources

Explore more TypeScript patterns and best practices in our web development guides covering React patterns, CSS variables, and form structure.

Troubleshooting Common Issues

Why am I getting undefined values during iteration?

When iterating numeric enums, accessing values through string keys can return undefined if the reverse mapping is unexpectedly absent. Use type guards to ensure you're only accessing valid enum members.

Why does Object.values() return both numbers and strings?

Numeric enums create reverse mappings at runtime. Object.values() returns both the numeric values and the string names because both exist as properties. Filter by type if you need only one type of value.

Why can't I iterate over my const enum?

Const enums are completely inlined at compile time and don't exist as JavaScript objects at runtime. Use regular enums when iteration is required, or use object literals with 'as const' instead.

How do I iterate only over enum keys or values?

Use Object.keys() for only names, or filter Object.values() to exclude reverse mappings. For numeric enums, filter out numeric keys: Object.keys(enum).filter(k => isNaN(Number(k))).

Ready to Level Up Your TypeScript Skills?

Our team specializes in building type-safe, performant web applications with modern TypeScript patterns. Get expert guidance on type systems, enum patterns, and scalable architecture.

Conclusion

Iterating over TypeScript enums requires understanding their runtime behavior and choosing the appropriate method for your use case. Here's a quick reference:

For most applications: Object.keys() combined with type guards provides the best balance of type safety and simplicity.

For memory efficiency: Use for...in loops when iterating large enums in performance-critical code.

For readability: Combine Object.entries() with for...of for clean, modern TypeScript code.

Remember: Reserve const enums for performance-critical direct access scenarios where iteration isn't needed.

By following the patterns and best practices outlined in this guide, you can write robust, maintainable code that effectively handles enum iteration in your TypeScript projects.


Sources

  1. LogRocket: How to iterate over enums in TypeScript - Comprehensive coverage of iteration methods
  2. TypeScript Handbook: Enums - Official documentation on enum behavior