Advanced Error Handling Techniques in TypeScript
Effective error handling is crucial for building robust TypeScript applications. Beyond basic try-catch blocks, TypeScript provides several advanced techniques to handle errors gracefully and ensure code reliability. This article explores some of these advanced error handling strategies.
1. Custom Error Classes
Creating custom error classes allows you to represent different types of errors more accurately. Custom errors can include additional properties or methods, which can help in identifying and handling specific issues.
class CustomError extends Error {
constructor(public message: string, public code: number) {
super(message);
this.name = 'CustomError';
}
}
function throwError() {
throw new CustomError('Something went wrong', 500);
}
try {
throwError();
} catch (error) {
if (error instanceof CustomError) {
console.error(`Error: ${error.message}, Code: ${error.code}`);
} else {
console.error('Unexpected error:', error);
}
}
In this example, CustomError
extends the built-in Error
class and adds a code
property to specify an error code.
2. Error Handling in Asynchronous Code
Asynchronous code often requires special handling for errors. Using async
and await
along with try-catch
blocks can simplify error handling in asynchronous operations.
async function fetchData(url: string): Promise {
try {
const response = await fetch(url);
if (!response.ok) {
throw new CustomError('Failed to fetch data', response.status);
}
const data = await response.json();
console.log(data);
} catch (error) {
if (error instanceof CustomError) {
console.error(`Error: ${error.message}, Code: ${error.code}`);
} else {
console.error('Unexpected error:', error);
}
}
}
fetchData('https://api.example.com/data');
This example demonstrates handling errors from an asynchronous fetch
call using async
, await
, and try-catch
.
3. Error Boundaries in React with TypeScript
When working with React and TypeScript, error boundaries help catch errors in the component tree and display a fallback UI. Implementing error boundaries with TypeScript ensures type safety and proper error handling.
import React, { Component, ErrorInfo } from 'react';
interface Props {}
interface State {
hasError: boolean;
}
class ErrorBoundary extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(): State {
return { hasError: true };
}
componentDidCatch(error: Error, errorInfo: ErrorInfo): void {
console.error('Error caught by boundary:', error, errorInfo);
}
render() {
if (this.state.hasError) {
return <h1>Something went wrong.</h1>
}
return this.props.children;
}
}
export default ErrorBoundary;
In this React example, the ErrorBoundary
component catches errors in its child components and displays a fallback UI if an error occurs.
4. Using Type Guards for Error Types
Type guards help narrow down the type of an error in TypeScript. This is particularly useful when handling errors with different types or from various sources.
function isCustomError(error: any): error is CustomError {
return error instanceof CustomError;
}
try {
throw new CustomError('Example error', 400);
} catch (error) {
if (isCustomError(error)) {
console.error(`CustomError: ${error.message}, Code: ${error.code}`);
} else {
console.error('Unknown error:', error);
}
}
The isCustomError
function is a type guard that helps determine if the caught error is an instance of CustomError
.
5. Centralized Error Handling
For large applications, centralizing error handling can simplify error management and ensure consistency. This can be done using middleware in Express.js or global error handlers in other frameworks.
import express, { Request, Response, NextFunction } from 'express';
const app = express();
app.use((err: any, req: Request, res: Response, next: NextFunction) => {
console.error('Centralized Error:', err.message);
res.status(500).send('Internal Server Error');
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});
This example demonstrates a centralized error handler for an Express.js application. It catches all errors and responds with a generic message.
Conclusion
Advanced error handling techniques in TypeScript enhance the robustness of your applications by providing more control over error management. Custom error classes, handling asynchronous errors, using error boundaries in React, type guards, and centralized error handling are essential strategies for effective error management. Implementing these techniques will lead to more maintainable and reliable code.