JavaScript Custom Errors: Error, TypeError, ReferenceError, and SyntaxError

JavaScript provides built-in error classes that help developers identify and handle different types of problems in their programs. You can also create custom errors to represent application-specific situations, such as invalid user input, failed payments, or unavailable resources.

Understanding JavaScript error types makes debugging easier and helps you write more reliable applications.

In this tutorial, you will learn how the Error, TypeError, ReferenceError, and SyntaxError classes work, how to create custom errors, and how to handle them using try...catch.

What Are Errors in JavaScript?

An error is an object or value used to represent a problem that occurs during program execution or code processing.

JavaScript provides several built-in error classes. Each class represents a particular category of problem.

For example:

try {
  const user = null;

  console.log(user.name);
} catch (error) {
  console.log(error.name);
  console.log(error.message);
}

Output:

TypeError
Cannot read properties of null (reading 'name')

The code attempts to access a property of null, so JavaScript throws a TypeError. The catch block receives the error and displays its name and message.

The Error Class in JavaScript

The Error class is the standard base class for creating error objects in JavaScript. You can use it to represent a problem that your program detects.

Syntax

new Error(message);

The message argument describes the problem. An error object also commonly has properties such as name and stack.

Example of Error

const error = new Error("Something went wrong");

console.log(error.name);
console.log(error.message);

Output:

Error
Something went wrong

The Error constructor creates an error object, but it does not throw the error automatically.

Throwing an Error

To interrupt the normal execution path and raise an exception, use the throw statement.

function withdraw(balance, amount) {
  if (amount > balance) {
    throw new Error("Insufficient balance");
  }

  return balance - amount;
}

try {
  console.log(withdraw(1000, 1500));
} catch (error) {
  console.log(error.message);
}

Output:

Insufficient balance

The function checks whether the withdrawal amount exceeds the available balance. If it does, the function throws an error, which is handled by catch.

When Should You Use Error?

Use the standard Error class when you need to report a general problem that does not require a more specific built-in or custom error type.

Examples include:

  • An operation cannot be completed.
  • A required resource is unavailable.
  • An application encounters an unexpected condition.
  • A function cannot continue because a required condition is not satisfied.

The TypeError Class in JavaScript

A TypeError occurs when an operation cannot be performed because a value has an inappropriate type or does not support the required operation.

Common examples include accessing a property on null, calling a non-function value, or assigning a value to a read-only property.

Example of TypeError

const user = null;

console.log(user.name);

Output:

TypeError: Cannot read properties of null (reading 'name')

The exact error message may vary between JavaScript engines.

The error occurs because null does not represent an object whose properties can be accessed.

Example of Calling a Non-Function

const result = 100;

try {
  result();
} catch (error) {
  console.log(error.name);
}

Output:

TypeError

The variable result contains a number, not a function. Calling it as a function causes a TypeError.

How to Handle TypeError

You can handle a TypeError using try...catch.

try {
  const user = null;

  console.log(user.name);
} catch (error) {
  if (error instanceof TypeError) {
    console.log("Invalid operation on a value");
  } else {
    console.log("An unexpected error occurred");
  }
}

Output:

Invalid operation on a value

For predictable situations, it is often better to validate values before using them rather than relying on exceptions for normal control flow.

The ReferenceError Class in JavaScript

A ReferenceError occurs when JavaScript cannot resolve a variable or reference that the code attempts to access.

One common cause is using a variable that has not been declared.

Example of ReferenceError

try {
  console.log(userName);
} catch (error) {
  console.log(error.name);
  console.log(error.message);
}

Output:

ReferenceError
userName is not defined

Because userName has not been declared, JavaScript throws a ReferenceError.

ReferenceError with let and const

Variables declared using let and const cannot be accessed before their declarations have been initialized.

try {
  console.log(age);

  let age = 25;
} catch (error) {
  console.log(error.name);
}

Output:

ReferenceError

This happens because age is in its temporal dead zone before its declaration is initialized.

How to Prevent ReferenceError

Make sure variables are declared in the correct scope and accessed only after initialization.

const userName = "Karim";

console.log(userName);

Output:

Karim

Understanding JavaScript Hoisting and variable scope can help you avoid common reference errors.

The SyntaxError Class in JavaScript

A SyntaxError occurs when JavaScript encounters invalid syntax while parsing code or when a syntax-sensitive operation attempts to parse invalid input.

Examples include missing quotation marks, invalid expressions, and malformed JSON.

Example of SyntaxError with JSON.parse()

try {
  JSON.parse("{name: 'Karim'}");
} catch (error) {
  console.log(error.name);
}

Output:

SyntaxError

The string is not valid JSON because JSON requires double quotation marks around property names and string values.

Correct JSON Example

const user = JSON.parse('{"name": "Karim", "age": 25}');

console.log(user.name);
console.log(user.age);

Output:

Karim
25

SyntaxError in JavaScript Source Code

Consider this invalid declaration:

const name = ;

The code contains a syntax error because no expression appears after the assignment operator.

A syntax error that prevents a script from being parsed cannot normally be caught by placing a try...catch around the invalid statement in that same script. The script must first be syntactically valid to execute its error-handling code.

However, syntax errors produced while parsing dynamically supplied code or JSON can be caught if the parsing operation occurs inside a try block.

What Are Custom Errors in JavaScript?

Custom errors are error classes that developers create to represent specific problems in an application.

Built-in errors such as Error and TypeError cover common language-level failures. Custom errors make it possible to describe application-specific situations more clearly.

For example, an online shopping application might define custom errors for:

  • Invalid payment details.
  • Insufficient account balance.
  • An unavailable product.
  • An expired session.
  • Failed authentication.

Creating a Custom Error Class

You can create a custom error class by extending the built-in Error class.

class ValidationError extends Error {
  constructor(message) {
    super(message);

    this.name = "ValidationError";
  }
}

const error = new ValidationError("Email is required");

console.log(error.name);
console.log(error.message);

Output:

ValidationError
Email is required

Understanding the Code

  • class ValidationError extends Error creates a class that inherits from Error.
  • constructor(message) receives the error message.
  • super(message) initializes the base Error object.
  • this.name = "ValidationError" assigns a descriptive name to the custom error.

The resulting error object can be handled like other JavaScript errors.

Throwing a Custom Error

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

function registerUser(email) {
  if (!email) {
    throw new ValidationError("Email is required");
  }

  return "Registration successful";
}

try {
  console.log(registerUser(""));
} catch (error) {
  console.log(error.name);
  console.log(error.message);
}

Output:

ValidationError
Email is required

The function throws a ValidationError when the email is missing. The catch block receives the error and displays its name and message.

Handling Custom Errors with instanceof

The instanceof operator checks whether an error object belongs to a particular class or inherits from it.

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

try {
  throw new PaymentError("Payment failed");
} catch (error) {
  if (error instanceof PaymentError) {
    console.log("Handle payment failure");
  } else {
    console.log("Handle general error");
  }
}

Output:

Handle payment failure

This approach is useful when different errors require different responses.

Difference Between Error, TypeError, ReferenceError, and SyntaxError

These error classes represent different kinds of problems.

Error classPurposeExample
ErrorRepresents a general errorthrow new Error("Failed")
TypeErrorRepresents an invalid operation involving a value’s type or capabilitiesCalling a number as a function
ReferenceErrorRepresents an unresolved variable or referenceAccessing an undeclared variable
SyntaxErrorRepresents invalid syntax or malformed parsed inputInvalid JSON passed to JSON.parse()

The built-in error classes, including these examples, inherit from Error.

Best Practices for JavaScript Errors

Follow these practices when handling errors in your applications.

  • Use the built-in error class that best describes the problem.
  • Create custom error classes when application-specific error categories improve clarity.
  • Include useful error messages that explain what went wrong.
  • Use instanceof when you need to handle specific error classes.
  • Avoid swallowing errors silently.
  • Validate user input before processing it.
  • Do not expose passwords, tokens, or other sensitive information in error messages.
  • Preserve the original error when wrapping it in another error, when appropriate.

For example, modern JavaScript supports the cause option for retaining an underlying error:

try {
  JSON.parse("{invalid}");
} catch (error) {
  throw new Error("Unable to load user data", {
    cause: error
  });
}

This example creates a new, more descriptive error while preserving the original parsing error in the cause property. If the new error is not caught by a surrounding handler, it will propagate as an unhandled exception.

Common Mistakes When Using JavaScript Errors

Creating an Error Without Throwing It

Creating an error object does not automatically raise an exception.

const error = new Error("Something went wrong");

console.log(error.message);

Output:

Something went wrong

To raise an exception, use throw error.

Using the Wrong Error Type

Use a suitable built-in error class or a custom class to make failures easier to understand. Do not use TypeError for every application-level problem simply because an operation failed.

Forgetting super() in a Custom Error Class

When a custom class extends Error, call super(message) to initialize the base error object.

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

Assuming Every Error Can Be Caught Locally

A try...catch block handles exceptions thrown within its execution. It does not automatically catch errors from unrelated asynchronous callbacks or unawaited promise rejections.

For asynchronous operations, use appropriate promise rejection handling or try...catch with await inside an async function.

FAQs About JavaScript Error Classes

What is the difference between Error and TypeError?

Error is the general error class. TypeError is a more specific built-in error used when an operation cannot be performed because a value has an inappropriate type or capability.

When does JavaScript throw a ReferenceError?

JavaScript throws a ReferenceError when it cannot resolve a reference, such as when code tries to access an undeclared variable.

When does JavaScript throw a SyntaxError?

A SyntaxError occurs when code or input being parsed violates the relevant syntax rules. Examples include invalid JavaScript syntax and malformed JSON passed to JSON.parse().

How do you create a custom error in JavaScript?

Create a class that extends Error, call super(message) in its constructor, and optionally set a descriptive name property.

Why should developers use custom errors?

Custom errors make application-specific failures easier to identify, report, test, and handle differently from other errors.

Can you catch a TypeError in JavaScript?

Yes. If a TypeError is thrown during execution within a try block, the associated catch block can handle it.

Are custom errors part of JavaScript’s built-in error classes?

No. Custom errors are defined by developers for their applications. They usually extend the built-in Error class.

Conclusion

JavaScript provides several built-in error classes for identifying problems during parsing and execution. Error represents general problems, TypeError indicates invalid operations involving values, ReferenceError identifies unresolved references, and SyntaxError represents invalid syntax or parsed input.

Custom error classes allow you to represent application-specific failures with meaningful names and messages.

By choosing appropriate error types, creating custom errors when necessary, and handling exceptions carefully with try...catch, you can make your JavaScript applications easier to debug and maintain.