JavaScript: Errors, Types, Properties

In this blog, I am going to talk about types of errors in Javascript. So grab a seat, have popcorns ready.

Javascript throws runtime errors and today we are going to see how to read, understand and use those errors in your code

Error:

In JS, an Error is an object. It has a Class Error, which has a constructor Error(). This is the generic error class in JS.

There are various types of errors that means there are various Classes of Errors.

So we can create Error objects from such constructors.

The generic constructor Error takes one argument (a message that will be used to describe the error)

//as Error is an object we can create it from its class' constructor
let newError = new Error("MyMessage for the error");
//now this newError is an instance(object) of class Error

So yes, you are right, if it's an object and also it has a Class, it should have properties too.

Standard Properties of Error Object:

  1. name -

    By default, Error instances are given the name "Error". All the instances of Class Error will have a name property as "Error".
  2. message -

    The message property is a human-readable description of the error. Contains brief information of the error.
  3. toString-

    You might be thinking we have the toString method for Objects too. But the Error object overrides the Object.prototype.toString(). In the background, it combines the name and message and converts them into a string.

These are 3 standard properties, there are other non-standard properties too but they might not be supported by some browsers.

Let's check below example

console.log(newError)
Uncaught Error: MyMessage for the error
    at <anonymous>:1:13

See the 1st word in the above error- Uncaught: it means your error was not handled using the catch keyword.

Next word is- Error: It is the value of the name property of the Error.

The next part is - MyMessage for the error: It is the value of the message property in the Error.

The next part is - at <anonymous>:1:13: This is the very important part, this is a stack trace, it shows where the error occurred, will talk about this in detail in later part of the blog.

So the above statement is just all the properties of Error showed together.

toString():

toString method when called on Error, will return a string like - name: message

If the name property value is undefined, it returns the string with name value as Error if the message property value is undefined, it returns the string with message value as an empty string ``

We will see one example of the toString() method.

var error1 = new Error('Bad operation');
console.log(error1.name) //Error 
//As it is an instance of Error class
console.log(error1.message) //Bad operation
console.log(error1.toString()); // 'Error: Bad operation'

var error2 = new Error('Bad operation');
error2.name = undefined;
//assigned undefined to error2 name property
console.log(error2.toString()); // 'Error: Bad operation'
//toString will return "Error" for undefined name

var error3 = new Error('Bad operation');
error3.name = 'hello';
error3.message = undefined;
//assigned undefined to error3 message property
console.log(error3.toString()); // 'hello'
//toString will return empty string for undefined message

Other than the generic Error constructor, there are other core error constructors in JavaScript. We will learn some of them in this blog.

1. RangeError :

The RangeError object is thrown when a value is not in the set or range of allowed values.

Constructor: RangeError()

Properties:

  1. message: RangeError should provide its own message property
  2. name: by default RangeError name property has the value "RangeError". Both the properties are inherited from the Error class
function checkAge(n)
{
    try{
        if( !(n >= 18) )
        {
            throw new RangeError("Age must be greater than 18 to sign up")
        }
    }catch(error) { 
         console.error(error);
    }
}
checkAge(13)
// RangeError: Age must be greater than 18 to sign up
// at checkAge (<anonymous>:6:19)
// at <anonymous>:1:1

2. ReferenceError:

ReferenceError object is thrown when a non-existing variable is referenced or used in your code.

Constructor: ReferenceError()

Properties:

  1. message: ReferenceError should provide its own message property
  2. name: by default ReferenceError name property has the value "ReferenceError". Both the properties are inherited from the Error class
let name="Ankita"
function printFullName( ) {
    try{
         console.log(`${name} ${surname}`);
    } catch( error ){
         console.error(error)
    }
}
printFullName( );
//ReferenceError: surname is not defined
//  at printFullName (<anonymous>:4:33)
//  at <anonymous>:9:1

3. SyntaxError:

The SyntaxError object is thrown when a program contains syntactically invalid code.

Constructor: SyntaxError()

Properties:

  1. message: SyntaxError should provide its own message property
  2. name: by default SyntaxError name property has the value "SyntaxError". Both the properties are inherited from the Error class
const printName = (){
    console.log("Ankita");
}
//Above arrow function has fat arrow missing, it will throw below error
//Uncaught SyntaxError: Unexpected token ')'

4. TypeError:

The TypeError object is thrown when an operation could not be performed, mostly when a value is not of the expected type.

Constructor: TypeError()

Properties:

  1. message: TypeError should provide its own message property
  2. name: by default TypeError name property has the value "TypeError". Both the properties are inherited from the Error class
// This is 1st kind of TypeError, where we try to change a value that cannot be changed
const marks = 200;
const totalMarks = 250;
marks = marks * 100 / totalMarks;
//Uncaught TypeError: Assignment to constant variable.
//   at <anonymous>:1:7

//This is 2nd kind of TypeError. If an operand/argument is passed to a operator/function whose type is not compatible with the operator/function.
//below code tries to apply spread operator on a number, hence it throws an TypeError
let number = 9;
let numberSpreaded = [...number];
// Uncaught TypeError: number is not iterable
//   at <anonymous>:1:26


//This is 3rd kind of TypeError, when a value is used in an inappropriate way
//below reduce method can be called on array, but instead we are calling it on a number, it will throw an TypeError
let arr= 9;
arr.reduce((sum,num)=>sum+num, 0);
// Uncaught TypeError: arr.reduce is not a function
//    at <anonymous>:2:5

5. URIError:

URIError is thrown when a global URI method is used in a wrong way.

e.g. The decodeURI() function takes encoded URI as an argument, it throws an URIError when the encoded URI contains invalid character sequences.

Constructor: URIError()

Properties:

  1. message: URIError should provide its own message property
  2. name: by default URIError name property has the value "URIError". Both the properties are inherited from the Error class
try {
  let a = decodeURI('%AN%KI%');
} catch(e) {
  console.error(e);
}
//URIError: URI malformed
//    at decodeURI (<anonymous>)
//    at <anonymous>:2:11

Selective Catching

Let's see an example, where we handle the error using try-catch block. What if we want to handle only the TypeError and not the syntax error. We can do that easily, as we know all the errors are instances of their class. we can check their class and find out which type of error our try block has.

function sumOfNumbersInArray (arrayOfNumbers) {
    try{
       return arrayOfNumbers.reduce((sum, num)=>sum+num, 0);
    } catch(error){
        if (error instanceof TypeError)
        console.error("Invalid type. This function works with arrays only!");
        else
        throw error
    }
}
sumOfNumbersInArray(3);
// Invalid type. This function works with arrays only!

function sumOfNumbersInArray (arrayOfNumbers) {
    try{
       return arrayOfNumbersss.reduce((sum, num)=>sum+num, 0);
    } catch(error){
        if (error instanceof TypeError)
        console.error("Invalid type. This function works with arrays only!");
        else
        throw error
    }
}
//In the above code I miss-typed the arrayOfNumbers variable, it throws an error(else block), as that error is ReferenceError and is not an instance of TypeError
//Uncaught ReferenceError: arrayOfNumbersss is not defined
//    at sumOfNumbersInArray (<anonymous>:3:8)
//    at <anonymous>:1:1

Stack Trace

Let's talk about stack trace now.

consider below example. It has 3 functions, function A calls B, and function B calls C.

function A () {
    try{    
        console.log("I am A, I will handle the error, and invoking B");
        B();
    } catch(error){
        console.error(error);
    }
}
function B () {
    console.log("I am B, and invoking C");
    C();
}
function C (){
    console.log("I am C and I have an error");
    throw new Error("fatal error");
}
A();
// I am A, I will handle the error, and invoking B
// I am B, and invoking C
// I am C and I have an error
// Error: fatal error
//    at C (<anonymous>:15:11)
//    at B (<anonymous>:11:5)
//    at A (<anonymous>:4:9)
//    at <anonymous>:17:1

In function A we are handling the error, but in C error is thrown, as soon as the error is thrown in C, it stops executing further and control comes to the point where it was invoked, that means in function B. Function B also stops executing and control comes to the point where it was invoked, that means in function A. Now function A sees the catch block and the error gets caught there and now program runs further without any interruption.

Now that error tells information about type of error, message of the error and stack trace.

Stack trace information is stored in the stack property and can be helpful when trying to debug a problem. it tells us the function name where the error occurred and which functions made the failing call. States what all things are there in the stack at the time when the error occurred.

So this was all about errors in javascript. Let me know in the comments if you found this blog helpful !!

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

References: