Harnessing TypeScript's Type Guards for Smarter Error Handling

Learn how to use TypeScript's type guards to improve error handling in your applications with clear, beginner-friendly examples.

When working with errors in TypeScript, it's common to encounter different kinds of error objects. Sometimes you might receive a standard Error, but other times the error could be a string, null, or even a custom error type. Using TypeScript's type guards helps you write safer, smarter error-handling code by checking the error type before accessing its properties.

A type guard is a function that checks the type of a value at runtime and informs the TypeScript compiler about it. This way, TypeScript can narrow down the type and provide better type checking and autocomplete suggestions.

Let's start with a simple example of a custom type guard function to check whether an error is an instance of the standard Error class.

typescript
function isError(error: unknown): error is Error {
  return error instanceof Error;
}

In this function, `error is Error` tells TypeScript that when this function returns true, the `error` variable should be treated as an Error object. Now, you can use this type guard in a try-catch block to handle different error types safely.

typescript
try {
  throw new Error('Something went wrong');
} catch (err) {
  if (isError(err)) {
    // Now TypeScript knows err is of type Error
    console.log('Error message:', err.message);
  } else {
    // Handle unknown error types
    console.log('Unexpected error:', err);
  }
}

You can extend this approach to more complex error handling by creating other custom type guards. For example, imagine your application throws custom error objects with extra properties.

typescript
interface NetworkError {
  message: string;
  code: number;
  url: string;
}

function isNetworkError(error: any): error is NetworkError {
  return (
    error &&
    typeof error.message === 'string' &&
    typeof error.code === 'number' &&
    typeof error.url === 'string'
  );
}

Using this guard, you can detect network errors and respond accordingly:

typescript
function handleError(error: unknown) {
  if (isNetworkError(error)) {
    console.log(`Network error at ${error.url} with code ${error.code}`);
  } else if (isError(error)) {
    console.log('General error:', error.message);
  } else {
    console.log('Unknown error:', error);
  }
}

By using type guards, your error handling becomes more precise, and TypeScript can help prevent bugs by making sure you only access properties that exist on the specific error type. This leads to clearer, safer, and easier-to-maintain code—especially in large applications.

In summary, start by defining simple type guard functions for your error types. Use them in your catch blocks or error handling code to validate the error type before accessing its properties. This technique leverages TypeScript’s strengths and makes your debugging and error handling smarter.