TypeScript Error Handling Strategies for Scalable System Design

Learn beginner-friendly TypeScript error handling strategies that help build scalable and maintainable system designs.

Error handling is a crucial part of building scalable and reliable applications. In TypeScript, handling errors efficiently helps prevent crashes and improves maintainability. This article introduces beginner-friendly error handling strategies with practical code examples, so you can design better systems that gracefully deal with failures.

### 1. Using try/catch Blocks The simplest way to handle errors in TypeScript is by using try/catch blocks. You wrap code that might throw an error inside try, and catch allows you to handle or log the error gracefully.

typescript
function parseJson(input: string): object | null {
  try {
    return JSON.parse(input);
  } catch (error) {
    console.error('Failed to parse JSON:', error);
    return null;
  }
}

const data = parseJson('{"name":"TypeScript"}');
console.log(data);

### 2. Using Result Types Instead of Throwing Errors Throwing errors works, but can sometimes make error tracking complicated in bigger systems. An alternative is to use "Result" types, which return either a success or error value explicitly.

typescript
type Result<T> = { success: true; value: T } | { success: false; error: string };

function safeDivide(a: number, b: number): Result<number> {
  if (b === 0) {
    return { success: false, error: "Division by zero" };
  }
  return { success: true, value: a / b };
}

const result = safeDivide(10, 0);
if (result.success) {
  console.log('Result:', result.value);
} else {
  console.error('Error:', result.error);
}

### 3. Creating Custom Error Classes For larger systems, defining custom error classes helps identify specific problems and handle them differently based on error types.

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

function validateAge(age: number) {
  if (age < 0 || age > 150) {
    throw new ValidationError('Age must be between 0 and 150.');
  }
  return true;
}

try {
  validateAge(-5);
} catch (err) {
  if (err instanceof ValidationError) {
    console.warn('Validation failed:', err.message);
  } else {
    console.error('Unknown error:', err);
  }
}

### 4. Using Async/Await with Proper Error Handling When dealing with asynchronous operations, always handle errors by using try/catch blocks around await calls or by handling promise rejections.

typescript
async function fetchData(url: string) {
  try {
    const response = await fetch(url);
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    const data = await response.json();
    return data;
  } catch (error) {
    console.error('Fetch error:', error);
    return null;
  }
}

fetchData('https://api.example.com/data');

### Summary By applying these beginner-friendly error handling strategies—try/catch blocks, Result types, custom errors, and proper async handling—you improve application robustness and scalability. These patterns help pinpoint and manage errors effectively, leading to cleaner and more maintainable TypeScript code.