Custom Errors

Pranay Chauhan
Last Updated: May 13, 2022

Introduction

We all use javascript in our web applications.

Have you ever faced the problem of displaying particular errors? 

We sometimes feel the need for errors that are not standardly defined in javascript.

We want to display errors in a personalized way. For example, if we are working with network applications, we may need HttpError, for database operations DbError, and so on. There are different types of errors that we want to define when working on different operations in an application. 

This is where custom errors in javascript come into the picture.

Let us have deep dive into the world of error handling in javascript.

What are custom errors?

Custom errors are the errors defined by a user to suit the different operations in the application. These errors are used to make errors more meaningful.

Our errors in javascript basically should support basic error properties, that is, message, name, and optionally, stack. But these errors can be provided with the other properties that we desire. For example, the HttpError objects have a statusCode property with values like 404 or 403 or 500.

In javascript, we can use the “throw” keyword with any argument; this implies that our custom error classes do not need to inherit from “Error.” But if we inherit from the “Error” class, it becomes possible to use “obj instanceof Error” to identify error objects. So it is always better to inherit from the “Error” class.

As we develop our application, our custom errors naturally form a hierarchy. For example, HttpTimeoutError may inherit from HttpError, and so on.

How to implement custom errors?

To implement custom errors, we will consider an example of a function readUser(json) that reads the json file with user data. 

Below is an example of how json format of a user looks:

let json = `{ "name": "John", "age": 30 }`;

 

We want to create a custom error that is appropriately shown when it receives a malformed JSON. It throws SyntaxError in that case. 

For the implementation, internally, we will use JSON.parse. But what do we do when the JSON data is syntactically correct? The JSON can be syntactically valid, but it is not necessarily the correct user data format. It can miss the necessary data. It may not have the name and age property in the JSON data.

The function readUser(json) will read the json data and validate the data. If there are no essential fields defined or the format is wrong, then it is an error, and it is not a SyntaxError as the data is syntactically correct. 

Let us call this error ValidationError. We will create a class for it which is inherited by the “Error” class. This type of error should also contain additional information about the field that violates the rules to validate the data.

Below is the approximate code for the “Error” class, to get an idea of what we are going to extend further in this article.

// The "pseudocode" for the built-in Error class defined by JavaScript itself
class Error {
 constructor(message) {
   this.message = message;
   this.name = "Error"; // (different names for different built-in error classes)
   this.stack = <call stack>;
 }
}

 

Now, we let us get into the implementation of our custom error, ValidationError.

class ValidationError extends Error {
 constructor(message) {
   super(message); // (1)
   this.name = "ValidationError"; // (2)
 }
}

function test() {
 throw new ValidationError("Whoops!");
}

try {
 test();
} catch(err) {
 alert(err.message); // Whoops!
 alert(err.name); // ValidationError
 alert(err.stack); // a list of nested calls with line numbers
}

In the above code, we extend the ValidationError class from the Error class. We call the parent constructor and set the message property to the message from the parent class. The parent class also makes the name property to “Error,” so we again change it to “ValidationError.”

Now, we will use the above defined custom error, into our function readUser(json)

class ValidationError extends Error {
 constructor(message) {
   super(message);
   this.name = "ValidationError";
 }
}

// Usage
function readUser(json) {
 let user = JSON.parse(json);

 if (!user.age) {
   throw new ValidationError("No field: age");
 }
 if (!user.name) {
   throw new ValidationError("No field: name");
 }

 return user;
}

// Working example with try..catch

try {
 let user = readUser('{ "age": 25 }');
} catch (err) {
 if (err instanceof ValidationError) {
   alert("Invalid data: " + err.message); // Invalid data: No field: name
 } else if (err instanceof SyntaxError) { // (*)
   alert("JSON Syntax Error: " + err.message);
 } else {
   throw err; // unknown error, rethrow it (**)
 }
}

In the above implementation, the try..catch block handles our ValidationError and the SyntaxError from JSON.parse.

Also, we use instanceof keyword instead of “==,” which is much better implementation, as in the future, we are going to extend our custom error. And instanceof check will continue to work for the new inheriting classes. Hence it is future-proof.

How to extend the custom error class?

The ValidationClass is very generic. Many things can go wrong. The properties present in the user JSON may be absent or in the wrong format. Let us create another class, PropertyRequiredError, specifically for absent properties. This class will carry the extra information for the property that is missing.

class ValidationError extends Error {
 constructor(message) {
   super(message);
   this.name = "ValidationError";
 }
}

class PropertyRequiredError extends ValidationError {
 constructor(property) {
   super("No property: " + property);
   this.name = "PropertyRequiredError";
   this.property = property;
 }
}

// Usage
function readUser(json) {
 let user = JSON.parse(json);

 if (!user.age) {
   throw new PropertyRequiredError("age");
 }
 if (!user.name) {
   throw new PropertyRequiredError("name");
 }

 return user;
}

// Working example with try..catch

try {
 let user = readUser('{ "age": 25 }');
} catch (err) {
 if (err instanceof ValidationError) {
   alert("Invalid data: " + err.message); // Invalid data: No property: name
   alert(err.name); // PropertyRequiredError
   alert(err.property); // name
 } else if (err instanceof SyntaxError) {
   alert("JSON Syntax Error: " + err.message);
 } else {
   throw err; // unknown error, rethrow it
 }
}

As shown in the above code implementation, The PropertyRequiredError is very easy to use, and we only need to pass the property name: new PropertyRequiredError(property). The constructor generates the easy-to-read message.

Please note that this.name in PropertyRequiredError constructor is again assigned manually. That may become a bit tedious – to assign this.name = <class name> in every custom error class. We can avoid it by making our own “basic error” class that assigns this.name = this.constructor.name. And then inherit all our custom errors from it.

Let’s call it MyError.

Here’s the code with MyError and other custom error classes, simplified:

class MyError extends Error {
 constructor(message) {
   super(message);
   this.name = this.constructor.name;
 }
}

class ValidationError extends MyError { }

class PropertyRequiredError extends ValidationError {
 constructor(property) {
   super("No property: " + property);
   this.property = property;
 }
}

// name is correct
alert( new PropertyRequiredError("field").name ); // PropertyRequiredError

Now custom errors are much shorter, especially ValidationError, as we removed the "this.name = ..." line in the constructor.

Frequently Asked Questions (FAQs)

What are custom errors?
Custom errors are the errors defined by a user to suit the different operations in the application. These errors are used to make errors more meaningful.

Which class do we generally inherit from in custom error classes?
We generally inherit our custom error class from the “Error” class.

Which keyword do we use to compare custom errors with mainly defined errors?
We use instanceof keyword to compare custom errors with standard errors.

Key Takeaways

We learned about the concept of custom errors in javascript. This exciting technique of extending errors in javascript allows us to create custom errors. This article also explained the implementation of custom errors and how to extend them.

You can also expand your knowledge by referring to these articles on Javascript.

For more information about the react framework for frontend development, get into the entire Frontend web development course.

Happy Learning!

Was this article helpful ?
0 upvotes

Comments

No comments yet

Be the first to share what you think