Creating Your Own Error Types

In large projects, a generic Error isn't specific enough. Custom error classes let you distinguish between different failure modes and react to them appropriately.

class ValidationError extends Error {
  constructor(message, field) {
    super(message);
    this.name = 'ValidationError';
    this.field = field; // Extra context: which field failed?
  }
}

class NetworkError extends Error {
  constructor(message, statusCode) {
    super(message);
    this.name = 'NetworkError';
    this.statusCode = statusCode;
  }
}

function validateUser(user) {
  if (!user.name) throw new ValidationError('Name is required', 'name');
  if (!user.email) throw new ValidationError('Email is required', 'email');
}

async function saveUser(user) {
  try {
    validateUser(user);
    const response = await fetch('/api/users', { method: 'POST', body: JSON.stringify(user) });
    if (!response.ok) throw new NetworkError('Server error', response.status);
  } catch (err) {
    if (err instanceof ValidationError) {
      showFieldError(err.field, err.message); // Handle validation
    } else if (err instanceof NetworkError) {
      showNetworkError(err.statusCode);       // Handle network
    } else {
      throw err; // Unknown — re-throw!
    }
  }
}
Note

Using instanceof lets you precisely identify the error type and respond differently to each kind.