Mastering Enums in JavaScript
Enums (short for “enumerations”) are a structured way to define sets of named constants. While JavaScript does not include native enum support, you can easily recreate powerful enum-like behavior using objects, Object.freeze(), Symbols, Maps, and even TypeScript.
This guide walks through multiple ways to use enums effectively, avoid magic values, and write clearer, safer code.
What Are Enums?
Enums allow developers to define a group of related constant values under a single namespace.
They help you:
- Avoid hard‑coded strings/numbers scattered across code
- Improve readability and maintainability
- Reduce bugs from typos or invalid values
- Provide predictable sets of values for application logic
1. Basic JavaScript Enum Using Objects
const Colors = {
RED: 'red',
GREEN: 'green',
BLUE: 'blue',
};
const favorite = Colors.RED;
console.log(favorite); // "red"A plain object works well as a simple enum substitute.
2. Immutable Enums with Object.freeze()
Make enums tamper‑proof:
const Directions = Object.freeze({
UP: 'up',
DOWN: 'down',
LEFT: 'left',
RIGHT: 'right',
});
Directions.UP = 'north'; // ignored or error in strict modeThis prevents accidental mutations.
3. Numeric Enums
Useful for mapping states or codes:
const Status = Object.freeze({
PENDING: 0,
IN_PROGRESS: 1,
COMPLETED: 2,
FAILED: 3,
});
console.log(Status.COMPLETED); // 24. Bidirectional Enum (Forward + Reverse Lookup)
const Days = Object.freeze({
MONDAY: 0,
TUESDAY: 1,
WEDNESDAY: 2,
THURSDAY: 3,
FRIDAY: 4,
0: 'MONDAY',
1: 'TUESDAY',
2: 'WEDNESDAY',
3: 'THURSDAY',
4: 'FRIDAY',
});
console.log(Days.MONDAY); // 0
console.log(Days[0]); // "MONDAY"Bidirectional enums provide extra flexibility, making them feel more like TypeScript enums.
5. Using Symbols for Safer, Unique Enum Values
Symbols produce unique, immutable values—ideal for preventing collisions.
const Animal = {
CAT: Symbol('cat'),
DOG: Symbol('dog'),
BIRD: Symbol('bird'),
};
console.log(Animal.CAT === Animal.DOG); // falsePerfect for state machines or event types.
6. Enum with Validation Logic
Enums often pair well with validation helpers:
const Roles = Object.freeze({
ADMIN: 'admin',
USER: 'user',
GUEST: 'guest',
});
function isValidRole(value) {
return Object.values(Roles).includes(value);
}
console.log(isValidRole('admin')); // true
console.log(isValidRole('manager')); // false7. Enums with Maps (Great for Metadata)
const HttpStatus = new Map([
['OK', 200],
['NOT_FOUND', 404],
['SERVER_ERROR', 500],
]);
console.log(HttpStatus.get('OK')); // 200Maps allow attaching metadata to enum keys.
8. Advanced: Enum Factory Function
You can dynamically create enums while enforcing immutability:
function createEnum(values) {
return Object.freeze(
values.reduce((acc, key) => {
acc[key] = key.toLowerCase();
return acc;
}, {})
);
}
const Events = createEnum(['CLICK', 'HOVER', 'FOCUS']);
console.log(Events.CLICK); // "click"9. Native Enums in TypeScript (for comparison)
If you use TypeScript, real enums are built in:
enum Permission {
READ = 'read',
WRITE = 'write',
DELETE = 'delete',
}
const p: Permission = Permission.READ;TypeScript compiles enums into JavaScript but provides full type‑safety during development.
When Should You Use Enums?
Enums are best when:
- You need a restricted set of valid values
- You want readable constants instead of scattered strings
- Multiple developers rely on shared values
- You need state machines, status codes, modes, or categories
Avoid enums when:
- Simple variables or Booleans are enough
- You expect values to change often (prefer config files)
Conclusion
Even without native enum support, JavaScript provides several flexible patterns to create enums using objects, Maps, Symbols, and Object.freeze(). For strict typing and safety, TypeScript enums offer a robust alternative.
By using enums, your code becomes clearer, safer, and easier to maintain—especially in large or collaborative projects.