All about Errors in Javascript

·

13 min read

What is an Error?

In Javascript, an error is an object which gets thrown when something goes unexpected in a program.

The error object has three properties

  • message - A message is a short description about the error
  • name - Name tells us the type of the error
  • stack - This gives the stack trace of functions that lead to the error.

Stack Trace is the representation of call stack at a particular point of time. This is highly helpful to understand the order of execution of program and track the error.

function A() {
    console.log("In A");
    function B() {
        console.log("In B");
        console.trace();
    }
    B();
    console.log("Just came out of B")
}
/*Output
In A
In B
console.trace
B    @    VM63:4
A    @    VM63:6
(anonymous)    @    VM77:1
Just came out of B */

From the output , we can see how the stack looks like at the point when console.trace() gets executed. The function A gets pushed to the stack , which calls the function B which then gets pushed to the stack , once B gets executed, it gets popped off and then prints Just came out of B

Creating an Error

Javascript provides us a built-in class named Error whose constructor has properties name, message and stack as seen above,

class Error{
      constructor(message){
            this.name = "Error";
            this.message = "message";
            this.stack = <call stack>
     }
}

Now, let's create an error and examine these properties. To create an error, we need to call the Error constructor using new keyword

const myerror = new Error("Something went wrong")

console.log(typeof myerror); //object
console.log(myerror.message); //Something went wrong
console.log(myerror.name); //Error
console.log(myerror.stack);
/*Error: Something went wrong
    at <anonymous>:1:17 */

Types of Error

There are different types of errors in javascript , I will here focus on the most common error types

  • Reference error

This error is something which all of us must have encountered. The ReferenceError object represents an error that occurs when we try to access something which is not existing in the program. For instance , trying to access a variable that has not been declared.

console.log(a)

/* Uncaught ReferenceError: a is not defined
    at <anonymous>:1:13 */

In the above program, trying to access the variable a that has not been declared in the program gives us reference error

  • TypeError

The TypeError object represents an error that occurs when an operation could not be performed because of the type mismatch. The particular place where we might have encountered this error is when trying to reassign the const variable. We know that const variables cannot be reassigned and when trying to do so , the JS Engine throws a TypeError

const a  = 5;
a = 7 ;

/*VM234:1 Uncaught TypeError: Assignment to constant variable.
    at <anonymous>:1:3*/
  • SyntaxError

    All of us surely must have encountered this error atleast when we started programming. This SyntaxError object represents an error that occurs because of the incorrect use of predefined syntax. This might happen because of our carelessness, for instance, initializing a variable with va instead of var or not closing the function with curly braces ( } ) , all these lead to Syntax error.

va a  = 7;
//Uncaught SyntaxError: Unexpected identifier

function a() {
  console.log(7)
//SyntaxError: Unexpected end of input
  • RangeError

    The RangeError object represents the error that occurs when a value is not in the specified range. Let's simulate a RangeError of our own, let us assume that we are creating a functionality that allows only users between the age of 18 and 24.
const playGame = (age) => {
    if(!(age>=18&&age<=24)){
           throw new RangeError("Sorry, Your age doesn't pass the criteria")
    }
    else{
           //do something
     }
}
try{
 playGame(15);
}

catch(err){
  console.log(err);
}


/* RangeError: Sorry, Your age doesn't pass the criteria
    at playGame (<anonymous>:3:18)
    at <anonymous>:10:2 */

For now, Let us ignore the keywords , try , catch and throw, but I am sure by the end of the blog we will be familiar with them . What the above program ideally does is that it checks whether the age of the user is between 18 and 24 , if the age is not in the specified range, it throws a RangeError.

Now that we are familiar with error and it's types , we will dive deep into exceptions and how do we handle them.

Exceptions

An exception is an unexpected event that occurs during the execution of a program which prevents the normal flow of the program execution.

Going back to the functionality that allows only users above 18,

const playGame = (age) => {
   //do something
}

playGame(15)

The above program works totally fine as there is no flaw in the code. This means that the program might be syntactically correct, but it does not satisfy the purpose of allowing only users that are above the age of 18. We need to raise an exception and handle the raised exception, else it messes up the whole point of creating this functionality.

Let's handle this exception now. But before moving on to Exception handling, let's understand what the jargons in exception handling mean

throw

throw statement is used to raise an exception when an unexpected event occurs

try - catch block

The main piece of code that needs to be tested goes in try block. The try block is followed by a catch block.

  • If the code in the try block throws any exception, it is then caught by the catch block and handled as needed.

finally block

This block is optional. The code in the finally block always gets executed no matter if there is an exception or not.

Now, I believe the above code in the RangeError example makes more sense

const playGame = (age) => {
    if(age<18) {
           throw new RangeError("Sorry, Your age doesn't pass the criteria")
    }
    else{
           //do something
     }
}
try{
 playGame(15);
}

catch(err){
  console.log(err);
}

finally{
   console.log("I will get executed no matter what")
}

/* RangeError: Sorry, Your age doesn't pass the criteria
    at playGame (<anonymous>:3:18)
    at <anonymous>:10:2 
I will get executed no matter what */

Let's see the flow of the program,

  • The control will initially be inside the try block
  • It then goes to the function playGame()
  • Inside the function, it checks whether the age is less than 18
  • If yes,
    1. the program throws an exception
    2. the flow goes to the catch block and prints the error and
    3. then the flow goes to finally block
  • If no ,
    1. the program executes the else block in the function and
    2. the flow goes to finally block

This way we have handled the case when the user gives an age that is lesser than 18.

Custom Error

We have seen the different types of built-in error in javascript. But what if we want to write an error of our own so that it makes more sense when building a functionality, In this part of the blog we are going to see how to write a custom error and it involves the object oriented programming concepts.

Let's assume, we want to validate the form details that the user enter to sign into an application. We need to check if the user has entered all the details , if he/she has missed any detail we need to raise an exception and let him/her know that a detail is missing.

To create a custom error, we need to inherit the built-in Error class and the code goes here,

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

So the above code creates a class named FormValidationError which inherits from its parent class Error. The super() method inherits all the properties of the parent class Error to the child class FormValidationError and we are overriding the name property of the parent class(Error) as FormValidationError in the constructor of FormValidationError class(i.e..,child class)

Now let's create a function that throws an exception when any one of the form details is missing from the user

const hasUserEnteredAllDetails = (formObject) =>  {
  const {name,email,password} = formObject  //destructuring
  if(!name){
    throw new ValidationError("Name field is empty")
  }
  if(!email){
    throw new ValidationError("Email field is empty")
  }
  if(!password){
    throw new ValidationError("Password field is empty")    
  }
  console.log("User has entered all details")
}

Let's create a formObject explicitly that has the email, name and password properties

const formObject = {
name:"sruthi",
email:"",
password:"sruthi"}

Note that the email property of the formObject is empty (falsy value)

Now, let's use try-catch block to validate the formObject

try{
  hasUserEnteredAllDetails(formObject)
}
catch(err){
  console.log(err)
}

Putting the pieces together

//class to create a custom error
class ValidationError extends Error {
  constructor(message){
    super(message);
    this.name = "ValidationError"; 
  }

//function to validate form details
function hasUserEnteredAllDetails(formObject) {
  const {name,email,password} = formObject //(1)
  if(!name){
    throw new ValidationError("Name field is empty")
  }
  if(!email){
    throw new ValidationError("Email field is empty")
  }
  if(!password){
    throw new ValidationError("Email field is empty")    
  }
  return true;
}

//Object that has the form details
const formObject = {name:"sruthi",email:"",password:"sruthi"}

//try catch block to test the form details
try{
  hasUserEnteredAllDetails(formObject)
}
catch(err){
  console.log(err)
}

//ValidationError : Email field is empty
at hasUserEnteredAllDetails (<anonymous>:3:18)

Now, we have created our custom error on our own and we have come to the end of the blog.

Writing this blog got me a lot of Ah! moments and I was able to learn a lot. Hope you have learnt something too!

I thank my mentor tanaypratap who was the motivation behind this blog.

Connect with me on Instagram, Twitter and Linkedin

Happy Coding!