TypeScript Error Handling Patterns

VerifiedSafe

Provides reusable error handling patterns for TypeScript applications including custom error classes, a Result type for explicit error propagation, React error boundaries, and async try-catch wrappers. Helps ensure robust error management with proper HTTP status codes and actionable error messages.

Sby Skills Guide Bot
DevelopmentIntermediate
6/2/2026
Claude CodeCursorWindsurfCopilotCodex
#error-handling#typescript#result-type#async#error-boundaries

Recommended for

Automated summary

This skill provides robust error handling patterns for TypeScript applications, including custom error classes, a Result type, React error boundaries, and async wrappers.

Strengths

  • Covers multiple approaches (error classes, Result, ErrorBoundary, tryCatch) for different contexts.
  • Custom error classes include codes and HTTP status codes for better semantics.
  • The Result pattern ensures type-safe handling of successes and failures.
  • Integrates React error boundaries for frontend applications.

Limitations

  • Some patterns (ErrorBoundary) depend on React and class components.
  • The Result pattern can add verbosity with type checks.
  • Does not cover advanced strategies like retries or fallbacks.
When to use it

Use this skill to structure error handling in complex TypeScript applications, especially REST APIs and React frontends.

When not to use it

Avoid this skill for simple scripts or projects where lightweight error handling is sufficient.

Security analysis

Safe
Quality score92/100

The skill provides educational code patterns for error handling in TypeScript. It does not include any destructive operations, obfuscated code, or instructions to execute external commands. The allowed tools are limited to safe file operations.

No concerns found

How this analysis is made

Examples

Custom error class
Create a custom error class NotFoundError that extends AppError with HTTP status code 404 and includes resource name and ID.
Result type pattern
Implement a Result type in TypeScript and use it for an async function that finds a user by ID, returning success or a NotFoundError.
React ErrorBoundary
Write a React ErrorBoundary class component that catches errors, logs them, and renders a fallback UI with a role='alert' div showing the error message.

name: error-handling-patterns description: Robust error handling patterns for TypeScript applications license: MIT compatibility: typescript 5+, nodejs 18+ allowed-tools: read_file write_file apply_patch search_with_context

Error Handling Patterns

Core Principles

  1. Fail fast - Detect and report errors early
  2. Fail safely - Errors shouldn't crash the application
  3. Fail informatively - Provide actionable error messages
  4. Recover gracefully - Handle expected failures

Custom Error Classes

// Base application error
export class AppError extends Error {
  constructor(
    message: string,
    public readonly code: string,
    public readonly statusCode: number = 500,
    public readonly details?: unknown
  ) {
    super(message);
    this.name = this.constructor.name;
    Error.captureStackTrace(this, this.constructor);
  }

  toJSON() {
    return {
      code: this.code,
      message: this.message,
      ...(this.details && { details: this.details }),
    };
  }
}

// Specific error types
export class ValidationError extends AppError {
  constructor(message: string, details?: Record<string, string>) {
    super(message, 'VALIDATION_ERROR', 400, details);
  }
}

export class NotFoundError extends AppError {
  constructor(resource: string, id: string) {
    super(`${resource} with id '${id}' not found`, 'NOT_FOUND', 404);
  }
}

export class UnauthorizedError extends AppError {
  constructor(message = 'Authentication required') {
    super(message, 'UNAUTHORIZED', 401);
  }
}

export class ForbiddenError extends AppError {
  constructor(message = 'Insufficient permissions') {
    super(message, 'FORBIDDEN', 403);
  }
}

Result Type Pattern

type Result<T, E = Error> =
  | { success: true; data: T }
  | { success: false; error: E };

// Helper functions
function ok<T>(data: T): Result<T, never> {
  return { success: true, data };
}

function err<E>(error: E): Result<never, E> {
  return { success: false, error };
}

// Usage
async function findUser(id: string): Promise<Result<User, NotFoundError>> {
  const user = await db.users.findUnique({ where: { id } });
  if (!user) {
    return err(new NotFoundError('User', id));
  }
  return ok(user);
}

// Consuming
const result = await findUser('123');
if (!result.success) {
  console.error(result.error.message);
  return;
}
const user = result.data; // Type-safe User

Error Boundaries (React)

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

interface Props {
  children: ReactNode;
  fallback?: ReactNode;
  onError?: (error: Error, info: ErrorInfo) => void;
}

interface State {
  hasError: boolean;
  error: Error | null;
}

export class ErrorBoundary extends Component<Props, State> {
  state: State = { hasError: false, error: null };

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

  componentDidCatch(error: Error, info: ErrorInfo) {
    this.props.onError?.(error, info);
  }

  render() {
    if (this.state.hasError) {
      return this.props.fallback ?? (
        <div role="alert">
          <h2>Something went wrong</h2>
          <pre>{this.state.error?.message}</pre>
        </div>
      );
    }
    return this.props.children;
  }
}

Async Error Handling

// Wrapper for async functions
function tryCatch<T>(
  promise: Promise<T>
): Promise<[null, T] | [Error, null]> {
  return promise
    .then((data) => [null, data] as [null, T])
    .catch((error) => [error as Error, null]);
}

// Usage
const [error, user] = await tryCatch(fetchUser(id));
if (error) {
  handleError(error);
  return;
}
// user is guaranteed to be defined here

// Multiple operations
async function processOrder(orderId: string) {
  const [orderError, order] = await tryCatch(getOrder(orderId));
  if (orderError) return err(orderError);

  const [paymentError] = await tryCatch(processPayment(order));
  if (paymentError) {
    await tryCatch(rollbackOrder(order));
    return err(paymentError);
  }

  return ok(order);
}

Retry Pattern

interface RetryOptions {
  maxAttempts: number;
  delayMs: number;
  backoff?: 'linear' | 'exponential';
  shouldRetry?: (error: Error) => boolean;
}

async function withRetry<T>(
  fn: () => Promise<T>,
  options: RetryOptions
): Promise<T> {
  const { maxAttempts, delayMs, backoff = 'exponential', shouldRetry } = options;
  let lastError: Error;

  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    try {
      return await fn();
    } catch (error) {
      lastError = error as Error;

      if (shouldRetry && !shouldRetry(lastError)) {
        throw lastError;
      }

      if (attempt < maxAttempts) {
        const delay = backoff === 'exponential'
          ? delayMs * Math.pow(2, attempt - 1)
          : delayMs * attempt;
        await sleep(delay);
      }
    }
  }

  throw lastError!;
}

// Usage
const data = await withRetry(() => fetchFromAPI(), {
  maxAttempts: 3,
  delayMs: 1000,
  shouldRetry: (err) => err.message.includes('timeout'),
});

Validation Errors

import { z } from 'zod';

function validateInput<T>(schema: z.ZodSchema<T>, input: unknown): Result<T, ValidationError> {
  const result = schema.safeParse(input);

  if (!result.success) {
    const details = result.error.flatten().fieldErrors;
    return err(new ValidationError('Invalid input', details));
  }

  return ok(result.data);
}

// Usage
const userSchema = z.object({
  email: z.string().email(),
  age: z.number().min(18),
});

const result = validateInput(userSchema, req.body);
if (!result.success) {
  return res.status(400).json(result.error.toJSON());
}

Logging Errors

interface ErrorContext {
  userId?: string;
  requestId?: string;
  path?: string;
  [key: string]: unknown;
}

function logError(error: Error, context: ErrorContext = {}) {
  const payload = {
    timestamp: new Date().toISOString(),
    name: error.name,
    message: error.message,
    stack: error.stack,
    ...(error instanceof AppError && { code: error.code }),
    ...context,
  };

  console.error(JSON.stringify(payload));

  // Send to error tracking service
  if (process.env.NODE_ENV === 'production') {
    // Sentry.captureException(error, { extra: context });
  }
}

Best Practices

  1. Create specific error types for different failure modes
  2. Use Result types for expected failures (validation, not found)
  3. Throw errors for unexpected failures (bugs, system errors)
  4. Include context in error messages for debugging
  5. Log at boundaries - API handlers, event processors
  6. Validate at boundaries - external input, API responses
  7. Implement retry for transient failures
  8. Use error boundaries in React for graceful degradation

From the file to a first result

The download contains exactly the content displayed on this page. This copy may differ from the latest version in the source repository.

View the source and supporting files

This download contains SKILL.md only. Any scripts, references, templates and dependencies are not included. Get the required files from the source, preserving their directory structure, before continuing.

1. Download the file

Download SKILL.md

From the root of a test project, save the file as .skill-downloads/typescript-error-handling-patterns-mma3jrtp/SKILL.md, or use the command below. This folder is for inspecting the skill before installation.

Command for macOS/Linux or WSL: requires a POSIX shell and curl. On Windows without WSL, use the download button and create the folder manually.

mkdir -p '.skill-downloads/typescript-error-handling-patterns-mma3jrtp' && curl --fail --show-error --location 'https://skills-guides.com/api/skills/typescript-error-handling-patterns-mma3jrtp/download' --output '.skill-downloads/typescript-error-handling-patterns-mma3jrtp/SKILL.md.part' && mv '.skill-downloads/typescript-error-handling-patterns-mma3jrtp/SKILL.md.part' '.skill-downloads/typescript-error-handling-patterns-mma3jrtp/SKILL.md'

2. Check prerequisites

Read SKILL.md before using it. If it references scripts or external services, inspect them and prepare the required access in your test environment.

Environment declared by the author: typescript 5+, nodejs 18+

Tools declared by the author (check availability in your agent): read_file write_file apply_patch search_with_context

No recognized prerequisites section in this file. This does not mean it is self-contained: libraries, tools and access requirements may be mentioned elsewhere in the content or source repository.

The platforms on this page are catalog indications. The paths below follow tool documentation; they do not prove this skill has been run in those tools. No minimum tool version is confirmed for this listing.

3. Load the skill

After inspection, copy the complete folder into your project using one of the commands below, then open a new tool session. The command stops if a folder with the same name already exists: compare the files before replacing it.

Claude Code
test -f '.skill-downloads/typescript-error-handling-patterns-mma3jrtp/SKILL.md' && test -s '.skill-downloads/typescript-error-handling-patterns-mma3jrtp/SKILL.md' && mkdir -p '.claude/skills' && test ! -e '.claude/skills/error-handling-patterns' && cp -R '.skill-downloads/typescript-error-handling-patterns-mma3jrtp' '.claude/skills/error-handling-patterns'

Cursor
test -f '.skill-downloads/typescript-error-handling-patterns-mma3jrtp/SKILL.md' && test -s '.skill-downloads/typescript-error-handling-patterns-mma3jrtp/SKILL.md' && mkdir -p '.cursor/skills' && test ! -e '.cursor/skills/error-handling-patterns' && cp -R '.skill-downloads/typescript-error-handling-patterns-mma3jrtp' '.cursor/skills/error-handling-patterns'

Codex
test -f '.skill-downloads/typescript-error-handling-patterns-mma3jrtp/SKILL.md' && test -s '.skill-downloads/typescript-error-handling-patterns-mma3jrtp/SKILL.md' && mkdir -p '.agents/skills' && test ! -e '.agents/skills/error-handling-patterns' && cp -R '.skill-downloads/typescript-error-handling-patterns-mma3jrtp' '.agents/skills/error-handling-patterns'

OpenSkills

OpenSkills must already be installed. The local install/read procedure is checked with OpenSkills 1.5.0, which requires Node.js ≥ 20.6.0. Catalog slugs are not resolved automatically.

Official OpenSkills installation
openskills install './.skill-downloads/typescript-error-handling-patterns-mma3jrtp'

openskills read 'typescript-error-handling-patterns-mma3jrtp'

Reading should display the installed file content and its base directory. It does not run the skill or install its dependencies.

For an agent that reads AGENTS.md, then sync the index. This command changes AGENTS.md: review the diff before using it.

openskills sync

Manual method or another tool: attach SKILL.md to a conversation, or open it in an agent that can read files. Include any required supporting resources. No universal folder is recognized by every tool.

4. Try a first task

Use a short, synthetic input: an API endpoint, a small document or an interface brief. In a new conversation, attach the file or give the agent its path, then adapt this request:

Read .skill-downloads/typescript-error-handling-patterns-mma3jrtp/SKILL.md and apply its instructions to this example: [describe a test input]. Identify any missing files, dependencies or access. Produce the result requested by the skill without inventing data or running unreviewed scripts.

Expected result: output matching the skill instructions, or a precise list of missing prerequisites. A read confirmation alone does not validate a first use. If an external resource is unavailable, that path remains untested.

Related skills