Mastering TypeScript Conditional Types for Advanced Error Handling

Learn how to use TypeScript conditional types to create robust and flexible error handling in your applications, perfect for beginners wanting to upgrade their TypeScript skills.

TypeScript is a powerful typed superset of JavaScript that helps you catch errors early and write safer code. One of its advanced features, conditional types, allows you to create types that depend on other types. This makes your error handling more precise and flexible, especially when working with different kinds of errors.

In this article, we'll explore how to use conditional types to handle errors conditionally, helping you write better TypeScript code that can adapt to various error scenarios. We'll start with a simple example and gradually build up to a more advanced use case.

Let's begin with a basic example. Suppose you have a function that fetches user data and returns different error shapes depending on the input. Conditional types can help you type these error results accurately.

typescript
type ApiResponse<T> = T extends 'user'
  ? { data: { id: string; name: string }; error?: never }
  : { data?: never; error: { message: string; code: number } };

function fetchData<T extends string>(endpoint: T): ApiResponse<T> {
  if (endpoint === 'user') {
    return { data: { id: '1', name: 'Alice' } } as ApiResponse<T>;
  } else {
    return { error: { message: 'Not Found', code: 404 } } as ApiResponse<T>;
  }
}

const userResponse = fetchData('user');
const errorResponse = fetchData('unknown');

In this example, the `ApiResponse` type uses a conditional type to determine the shape of the response based on the generic `T`. If `T` extends `'user'`, the response contains data and no error. Otherwise, the response contains an error and no data.

Now, let's look at a practical way to check errors and handle them safely without risking runtime errors.

typescript
function isError<T extends string>(response: ApiResponse<T>): response is { error: { message: string; code: number } } {
  return (response as any).error !== undefined;
}

if (isError(errorResponse)) {
  console.log(`Error: ${errorResponse.error.message} (${errorResponse.error.code})`);
} else {
  console.log(`User: ${userResponse.data.name}`);
}

The `isError` function is a type guard that narrows the type when an error is present. This way, TypeScript helps you safely access the error properties only when they exist.

To summarize, mastering TypeScript conditional types for error handling enables you to create smarter and safer code by tightly coupling error structure with data types. This ensures your functions clearly express their possible results, preventing bugs and improving maintainability.

Experiment with conditional types in your projects to get comfortable with this exciting feature. Soon you will find your error handling code cleaner and more robust!