Enumerations (Enums) are a common data structure feature in many programming languages. While JavaScript does not natively have this feature, we can quickly and easily add them.
What is an Enum?
Before we jump into the code and learn how to create an Enum, we should discuss what one is. Let's start with an Array. This Array is a list of possible error types that we could have in our web app.
const errors = ['Data Error', 'Lunch Error', 'Mom Error'];
While an Array allows us to store a list of error types that could be used later, it does require the developer to memorized the names and the order that they appear in the Array.
console.log( errors[0] );
//if you remember that zero refers to 'DataError'
//then you get the right value.
An Enumeration solves this problem by creating an uneditable list of values where each value has a String name as the key.
So, this can be more closely replicated with a JavaScript Object.
const errors = {
DATAERROR: 'Data Error',
LUNCHERROR: 'Lunch Error',
MOMERROR: 'Mom Error'
}
With an Object we avoid this issue of having to remember the position of each value.
console.log( errors['DATAERROR'] );
We still have the problem that any of the keys or values can be edited or removed.
So, an Enumeration is a list of key-value pairs that cannot be altered at runtime.
Creating JavaScript Enums
Accepting that an Object is a better choice than an Array for replicating an Enum, we need to solve the readonly issue. That brings us to the Object.freeze
method.
The Object.freeze
method that allows us take any object and change the configurable and writable property descriptors of every property so that no changes can be made to either the key or the value.
const errors = Object.freeze({
DATAERROR: 'Data Error',
LUNCHERROR: 'Lunch Error',
MOMERROR: 'Mom Error'
});
console.log( errors['LUNCHERROR'] );
//we can use either square bracket syntax
//or dot notation to access the values
console.log( errors.MOMERROR );
The use of const
will also prevent any new value being assigned to errors
and replacing the original Object.
Applications of Enums
As an example for how we could use our Enum we can imagine a simple web app that attempts to do some asynchronous tasks, in sequence, and when it fails any of them, it will throw an error that is specific to the task.
Promise.resolve(123)
.then(async (id)=>{
let url = `https://example.com/users/${id}`;
let resp = await fetch(url)
if(!resp.ok) throw new Error(errors['DATAERROR']);
return 'burger';
})
.then(async (food) => {
let isGood = await enjoyAsyncLunch(food);
if(!isGood) throw new Error(errors['LUNCHERROR']);
return true;
})
.then((ready) => {
if( !askMomForPermission() ) throw new Error(errors.MOMERROR);
})
.catch((err)=> {
console.error(`Reason for failure is ${err.message}`);
});
A nice added benefit of creating our Enum Object is that now, most IDEs, like VSCode, will be able to use code-complete to show you the possible values for errors.
Top comments (15)
Hi Steve Griffith,
thank you for your article.
I like how you switch from arrays to enums because I think it makes it easier to understand.
I'm not sure if that's intentional, but I also like the touch of humor found in your article.
Overall, I think the article is good enough to fit into an MDN documentation :D.
That was exactly my intent with the arrays.
Thanks for the hint with Object.freeze() when creating enum 💪
A well-written explanation. Thank your for sharing !!
Great article thanks
Thanks for the article Griffith.
I needed enums for my library project, your article helped me figure it out. Thanks
Thanks for sharing. Most concise and easily understandable explanation I’ve seen…
I would prefer Errortype.DATA_ERROR.
:) You can call them whatever you like.
I usually prefer the dot syntax too.
The valuable thing is having strings for names, instead of numbers, plus names and values that cannot be altered.
You can but you shouldn't, naming counts.
:D
Very true.
Why not just use TypeScript which has built-in enums and many more goodies.
Because a lot more people use JavaScript, not Typescript when developing.
When Typescript compiles back into JS, this is what it is doing.
Map 不香吗?
Maps have the same problem as objects - they can be altered. Object.freeze( ) gives the best version of an enum.