Create Custom JavaScript Error Objects
Tags: beginners, webdev, programming, javascript
Published: 2021-01-30 21:31:24

**Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62**

**Subscribe to my email list now at http://jauyeung.net/subscribe/**
In JavaScript, there’s an `Error` class to let code throw exceptions. We can create custom error classes by extending the `Error` class with our own class and custom logic to throw more specific errors.

The `Error` class has the `message` , `name` , and `stack` files that we inherit from. Of course, like any other class, we can define our own fields and methods inside it.

### Create a New Error Class

For example, to make data validation easier, we can make a `ValidationError` class which is thrown whenever our code encounters invalid data.

We can create a new class as follows:

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

As we can see, we used the `extends` keyword to indicate that we want to inherit from the `Error` class.

### Using Error Classes we Defined

After we defined our error classes, we can use our `ValidationError` class as follows:

```
try {
  let data = {
    email: 'abc'
  }
  if (!/[^@+@[^.+..+/.test(data.email)) {
    throw new ValidationError('invalid email', 'email error');
  }
} catch (ex) {
  console.log(ex);
}
```

In the `console.log` output, we should see ‘ValidationError: invalid email’ when we run the code.

We can also log the `message` , `name` , `stack` , and `type` properties individually:

```
try {
  let data = {
    email: 'abc'
  }
  if (!/[^@]+@[^.]+..+/.test(data.email)) {
    throw new ValidationError('invalid email', 'email error');
  }
} catch (ex) {
  const {
    message,
    name,
    stack,
    type
  } = ex;
  console.log(
    message,
    name,
    stack,
    type
  );
}
```

Then we should see:

*   `invalid email` logged for `message` 
*   `ValidationError` logged for `name` 
*   `ValidationError: invalid email
     at window.onload` for `stack`
*   `email error` logged for `type` 

As with another type of object, we can use the `instanceof` operator to check if it’s an instance of `ValidationError` as follows:

```
try {
  let data = {
    email: 'abc'
  }
  if (!/[^@]+@[^.]+..+/.test(data.email)) {
    throw new ValidationError('invalid email', 'email error');
  }
} catch (ex) {
  if (ex instanceof ValidationError){
   console.log('validation error occurred');
  }
}
```

Then we should see `‘validation error occurred’` logged since we threw an instance of the `ValidationError` class.

We can also look at the `name` property to discern different kinds of errors. For example, we can write:

```
try {
  let data = {
    email: 'abc'
  }
  if (!/[^@]+@[^.]+..+/.test(data.email)) {
    throw new ValidationError('invalid email', 'email error');
  }
} catch (ex) {
  if (ex.name === 'ValidationError') {
    console.log('validation error occurred');
  }
}
```

Then we get the same thing as above.

### Wrapping Exceptions

We can wrap lower-level exceptions, which are of instances of a subclass of a parent exception constructor and create higher-level errors.

To do this, we can catch and rethrow lower-level exceptions and catch them in higher-level code.

First, we have to create a parent and child classes which extend from the `Error` class as follows:

```
class FormError extends Error {
  constructor(message, type) {
    super(message);
    this.name = "FormError";
  }
}

class ValidationError extends FormError {
  constructor(message, type) {
    super(message);
    this.name = "ValidationError";
    this.type = type;
  }
}
```

In the code above, the `FormError` class extends the `Error` class and act as the parent class of the `ValidationError` class.

Then we write some code which makes use of our exception classes that we created:

```
const checkEmail = (data) => {
  try {
    if (!/[^@]+@[^.]+..+/.test(data.email)) {
      throw new ValidationError('invalid email', 'email error');
    }
  } catch (ex) {
    if (ex instanceof ValidationError) {
      throw new FormError(ex.name)
    } else {
      throw ex;
    }
  }
}

try {
  let data = {
    email: 'abc'
  }
  checkEmail(data);
} catch (ex) {
  if (ex instanceof FormError) {
    throw new FormError(ex.name)
  } else {
    throw ex;
  }
}
```

In the code above, we have a `checkEmail` function that throws an error if the `data.email` property has an invalid email. If a `ValidationError` is thrown, then we throw a new error.

Then we create a `try...catch` block after the `checkEmail` function to wrap around the function call and then we catch the exception in the `catch` block. We check for `FormError` instances and then throw a `FormError` if the caught exception is an instance of the `FormError` .

Otherwise, we rethrow the caught exception directly. We have this pattern so that we only deal with one kind of error at the top level, instead of checking for all kinds of error in top-level code.

The alternative is to have `if...else` statements to check for all kinds of errors at the top-level, which is more complex.

We can inherit from the `Error` class to create our own error classes. Then we can throw new error instances with the `throw` keyword.

In the class we define, we can add our own instance fields and methods.

When we catch the errors, we can use the `insranceof` operator to check for the type of error that’s thrown. We can also use the `name` property to do the same thing.

In our lower level code, we can catch errors of type of the sub-class error type and then rethrow an error of the instance of the parent class. Then we can catch the error of the higher-level type instead of checking for error instances of the lower-level type.