search ]

JavaScript throw Statement: Detailed Guide and Examples

The throw statement in JavaScript is used to generate custom errors. By using throw, you can create exceptions and handle them using the try…catch statement, allowing you to manage error handling in a more controlled and informative manner.

Basic Syntax

The basic syntax of the throw statement is as follows:

throw expression;

Do not place a line break between the throw keyword and the expression. JavaScript’s automatic semicolon insertion (ASI) will treat the line break as the end of the statement, resulting in throw; which is a syntax error.

The expression can be any valid JavaScript expression, but you should always throw an Error object (or a subclass of it). Throwing strings or numbers is technically possible but prevents access to stack traces and makes debugging harder.

Always use throw new Error('message') or a custom Error subclass. Throwing a plain string like throw 'something went wrong' produces no stack trace and cannot be reliably caught by type.

Example of throw Statement

Here is an example demonstrating the use of the throw statement:

function divide(a, b) {
    if (b === 0) {
        throw new Error('Division by zero is not allowed.');
    }
    return a / b;
}

try {
    console.log(divide(4, 2)); // Output: 2
    console.log(divide(4, 0)); // Throws error
} catch (error) {
    console.error(error.message); // Output: Division by zero is not allowed.
}

In this example, if the divisor b is zero, an error is thrown with a custom message. The try…catch block is used to handle the error and display the message.

Using throw with Custom Errors

You can create custom error objects by extending the built-in Error class. This allows you to define specific error types for your application:

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

function validateAge(age) {
    if (age < 0 || age > 120) {
        throw new ValidationError('Invalid age value.');
    }
    return true;
}

try {
    validateAge(150); // Throws ValidationError
} catch (error) {
    if (error instanceof ValidationError) {
        console.error(error.message); // Output: Invalid age value.
    } else {
        console.error('Unknown error:', error);
    }
}

Re-throwing Errors

In some cases, you might want to handle an error partially and then re-throw it to be handled by another try...catch block:

function parseJSON(jsonString) {
    try {
        return JSON.parse(jsonString);
    } catch (error) {
        if (error instanceof SyntaxError) {
            console.error('JSON parsing error:', error.message);
            throw error; // Re-throw the error
        } else {
            throw new Error('Unknown error');
        }
    }
}

try {
    parseJSON('invalid JSON');
} catch (error) {
    console.error('Caught an error:', error.message);
}

Error Chaining with cause

ES2022 introduced the cause property, which lets you attach the original error when throwing a new one. This preserves the full error chain without losing context:

async function fetchUser(id) {
    try {
        const response = await fetch(`/api/users/${id}`);
        if (!response.ok) {
            throw new Error(`HTTP ${response.status}`);
        }
        return await response.json();
    } catch (error) {
        throw new Error('Failed to fetch user', { cause: error });
    }
}

try {
    await fetchUser(42);
} catch (error) {
    console.error(error.message);       // "Failed to fetch user"
    console.error(error.cause.message); // "HTTP 404" (the original error)
}

The cause property is supported in all modern browsers and Node.js 16.9+.

Best Practices

Here are best practices for using the throw statement effectively:

  • Always throw Error objects: Never throw strings or numbers. Error objects provide a .message, .name, .stack, and optionally .cause – all essential for debugging.
  • Use descriptive messages: Write error messages that explain what went wrong and, where possible, include the invalid value or context.
  • Create custom error types: Define custom Error subclasses for domain-specific scenarios. This lets calling code use instanceof to handle different errors differently.
  • Use try…catch: Always handle thrown errors using try...catch blocks to prevent your application from crashing.
  • Re-throw with context: When catching and re-throwing, use the cause option to preserve the original error.
  • Don’t catch too broadly: Avoid catching Error at the top level and silently ignoring it. Log the error or handle it meaningfully.

FAQs

Common questions about the JavaScript throw statement:

Can I throw a string instead of an Error object?
Technically yes, but you should not. Throwing a string like throw 'error' produces no stack trace, no .name property, and no .cause support. Always throw new Error('message') or a custom Error subclass.
What is the difference between throw and return?
return exits a function and passes a value to the caller. throw exits a function and jumps to the nearest catch block in the call stack. If no catch exists, the program crashes. Use throw for exceptional conditions that the calling code must handle, not for normal control flow.
How does throw work inside an async function?
Inside an async function, throw rejects the returned Promise. The calling code can catch it with try...catch (inside another async function) or with .catch() on the Promise. The behavior is equivalent to calling Promise.reject(new Error('message')).
When should I create a custom Error class?
Create a custom Error class when your application needs to distinguish between different error types and handle them differently. For example, a ValidationError might trigger a user-facing message, while an HttpError might trigger a retry. If a generic Error with a descriptive message is enough, there is no need to create a custom class.
What is the cause property on Error objects?
The cause property (ES2022) lets you chain errors by attaching the original error to a new one: throw new Error('High-level message', { cause: originalError }). This preserves the full error context, making debugging easier when errors are caught and re-thrown across multiple layers.

Summary

The throw statement lets you generate custom errors that propagate up the call stack until a catch block handles them. Always throw Error objects or custom subclasses – never plain strings or numbers.

Extend the Error class to create domain-specific error types that calling code can identify with instanceof. Use the cause property (ES2022) to preserve the original error when re-throwing.

Watch out for automatic semicolon insertion – never place a line break between throw and the expression.

Join the Discussion
0 Comments  ]

Leave a Comment

To add code, use the buttons below. For instance, click the PHP button to insert PHP code within the shortcode. If you notice any typos, please let us know!

Savvy WordPress Development official logo