Menu
×
   ❮   
HTML CSS JAVASCRIPT SQL PYTHON JAVA PHP HOW TO W3.CSS C C++ C# BOOTSTRAP REACT MYSQL JQUERY EXCEL XML DJANGO NUMPY PANDAS NODEJS DSA TYPESCRIPT ANGULAR GIT POSTGRESQL MONGODB ASP AI R GO KOTLIN SASS VUE GEN AI SCIPY CYBERSECURITY DATA SCIENCE INTRO TO PROGRAMMING BASH RUST

TypeScript Error Handling


Robust error handling is crucial for building reliable TypeScript applications.

This guide covers everything from basic try/catch to advanced error handling patterns.


Basic Error Handling

Try/Catch Blocks

The foundation of error handling in TypeScript:

function divide(a: number, b: number): number {
  if (b === 0) {
    throw new Error('Division by zero');
  }
  return a / b;
}

try {
  const result = divide(10, 0);
  console.log(result);
} catch (error) {
  console.error('An error occurred:', error.message);
}
Try it Yourself »

TypeScript 4.0+ Note

In TypeScript 4.0 and later, the unknown type is the default type for catch variables. Always narrow the type before accessing properties.


Custom Error Classes

Creating Custom Error Classes

Extend the built-in Error class to create domain-specific errors:

class ValidationError extends Error {
  constructor(message: string, public field?: string) {
    super(message);
    this.name = 'ValidationError';
    // Restore prototype chain
    Object.setPrototypeOf(this, ValidationError.prototype);
  }
}

class DatabaseError extends Error {
  constructor(message: string, public code: number) {
    super(message);
    this.name = 'DatabaseError';
    Object.setPrototypeOf(this, DatabaseError.prototype);
  }
}

// Usage
function validateUser(user: any) {
  if (!user.name) {
    throw new ValidationError('Name is required', 'name');
  }
  if (!user.email.includes('@')) {
    throw new ValidationError('Invalid email format', 'email');
  }
}
Try it Yourself »

Type Guards for Errors

Type Predicates for Error Handling

Create type guards to safely work with different error types:

// Type guards
function isErrorWithMessage(error: unknown): error is { message: string } {
  return (
    typeof error === 'object' &&
    error !== null &&
    'message' in error &&
    typeof (error as Record).message === 'string'
  );
}

function isValidationError(error: unknown): error is ValidationError {
  return error instanceof ValidationError;
}

// Usage in catch block
try {
  validateUser({});
} catch (error: unknown) {
  if (isValidationError(error)) {
    console.error(`Validation error in ${error.field}: ${error.message}`);
  } else if (isErrorWithMessage(error)) {
    console.error('An error occurred:', error.message);
  } else {
    console.error('An unknown error occurred');
  }
}
Try it Yourself »

Type Assertion Pattern

For more complex error handling, consider using a type assertion function:

function assertIsError(error: unknown): asserts error is Error {
  if (!(error instanceof Error)) {
    throw new Error('Caught value is not an Error instance');
  }
}

try {
  // ...
} catch (error) {
  assertIsError(error);
  console.error(error.message); // TypeScript now knows error is Error
}

Async Error Handling

Handling Async/Await Errors

Proper error handling in async/await code requires wrapping await calls in try/catch blocks:

interface User {
  id: number;
  name: string;
  email: string;
}

// Using async/await with try/catch
async function fetchUser(userId: number): Promise {
  try {
    const response = await fetch(`/api/users/${userId}`);
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    return await response.json() as User;
  } catch (error) {
    if (error instanceof Error) {
      console.error('Failed to fetch user:', error.message);
    }
    throw error; // Re-throw to allow caller to handle
  }
}

// Using Promise.catch() for error handling
function fetchUserPosts(userId: number): Promise {
  return fetch(`/api/users/${userId}/posts`)
    .then(response => {
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }
      return response.json();
    })
    .catch(error => {
      console.error('Failed to fetch posts:', error);
      return []; // Return empty array as fallback
    });
}
Try it Yourself »

Unhandled Promise Rejections

Always handle promise rejections to prevent unhandled promise rejection warnings:

// Bad: Unhandled promise rejection
fetchData().then(data => console.log(data));

// Good: Handle both success and error cases
fetchData()
  .then(data => console.log('Success:', data))
  .catch(error => console.error('Error:', error));

// Or use void for intentionally ignored errors
void fetchData().catch(console.error);

Error Boundaries in React

React Error Boundary Component

Create an Error Boundary to catch JavaScript errors in React component trees:

import React, { Component, ErrorInfo, ReactNode } from 'react';

interface ErrorBoundaryProps {
  children: ReactNode;
  fallback?: ReactNode;
}

interface ErrorBoundaryState {
  hasError: boolean;
  error?: Error;
}

class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
  public state: ErrorBoundaryState = {
    hasError: false
  };

  public static getDerivedStateFromError(error: Error): ErrorBoundaryState {
    return { hasError: true, error };
  }

  public componentDidCatch(error: Error, errorInfo: ErrorInfo) {
    console.error('Uncaught error:', error, errorInfo);
    // Log to error reporting service
  }

  public render() {
    if (this.state.hasError) {
      return this.props.fallback || (
        <div className="error-boundary">
          <h2>Something went wrong</h2>
          <p>{this.state.error?.message}</p>
          <button onClick={() => this.setState({ hasError: false })}>
            Try again
          </button>
        </div>
      );
    }

    return this.props.children;
  }
}

// Usage
function App() {
  return (
    <ErrorBoundary fallback={<div>Oops! Something broke.</div>}>
      <MyComponent />
    </ErrorBoundary>
  );
}

Best Practices

Always Handle Errors

Never leave catch blocks empty.

At minimum, log the error:

// Bad: Silent failure
try { /* ... */ } catch { /* empty */ }

// Good: At least log the error
try { /* ... */ } catch (error) {
  console.error('Operation failed:', error);
}

Use Specific Error Types

Create custom error classes for different error scenarios:

class NetworkError extends Error {
  constructor(public status: number, message: string) {
    super(message);
    this.name = 'NetworkError';
  }
}

class ValidationError extends Error {
  constructor(public field: string, message: string) {
    super(message);
    this.name = 'ValidationError';
  }
}

Handle Errors at the Right Level

Handle errors where you have enough context to recover or provide a good user experience:

// In a data access layer
async function getUser(id: string): Promise {
  const response = await fetch(`/api/users/${id}`);
  if (!response.ok) {
    throw new NetworkError(response.status, 'Failed to fetch user');
  }
  return response.json();
}

// In a UI component
async function loadUser() {
  try {
    const user = await getUser('123');
    setUser(user);
  } catch (error) {
    if (error instanceof NetworkError) {
      if (error.status === 404) {
        showError('User not found');
      } else {
        showError('Network error. Please try again later.');
      }
    } else {
      showError('An unexpected error occurred');
    }
  }
}

Common Pitfalls

Not Handling Promise Rejections

Always handle promise rejections to prevent unhandled promise rejection warnings:

// Bad: Unhandled promise rejection
fetchData();

// Good: Handle the rejection
fetchData().catch(console.error);

Catching Without Proper Type Narrowing

In TypeScript 4.0+, caught errors are of type unknown:

// Bad: Error is of type 'unknown'
try { /* ... */ } catch (error) {
  console.log(error.message); // Error: Property 'message' does not exist on type 'unknown'
}

// Good: Narrow the type
try { /* ... */ } catch (error) {
  if (error instanceof Error) {
    console.log(error.message); // OK
  }
}

Swallowing Errors

Avoid silently catching and ignoring errors without proper handling:

// Bad: Error is silently ignored
function saveData(data: Data) {
  try {
    database.save(data);
  } catch {
    // Ignore
  }
}

// Better: Log the error and/or notify the user
function saveData(data: Data) {
  try {
    database.save(data);
  } catch (error) {
    console.error('Failed to save data:', error);
    showError('Failed to save data. Please try again.');
  }
}

Summary

Effective error handling in TypeScript involves:

  • Using try/catch blocks for synchronous code
  • Handling promise rejections with .catch() or try/catch with async/await
  • Creating custom error classes for domain-specific errors
  • Using type guards to safely work with error objects
  • Handling errors at the appropriate level in your application
  • Providing meaningful error messages to users

By following these practices, you can build more robust and maintainable TypeScript applications.




×

Contact Sales

If you want to use W3Schools services as an educational institution, team or enterprise, send us an e-mail:
[email protected]

Report Error

If you want to report an error, or if you want to make a suggestion, send us an e-mail:
[email protected]

W3Schools is optimized for learning and training. Examples might be simplified to improve reading and learning. Tutorials, references, and examples are constantly reviewed to avoid errors, but we cannot warrant full correctness of all content. While using W3Schools, you agree to have read and accepted our terms of use, cookie and privacy policy.

Copyright 1999-2025 by Refsnes Data. All Rights Reserved. W3Schools is Powered by W3.CSS.