javascript

examples

examples.js
/**
 * 4.7 Error Types and Custom Errors - Examples
 */

// ============================================
// BUILT-IN ERROR TYPES
// ============================================

console.log('=== Built-in Error Types ===');

// Error
try {
  throw new Error('Generic error message');
} catch (e) {
  console.log('Error:', e.name, '-', e.message);
}

// TypeError
try {
  const x = null;
  x.property;
} catch (e) {
  console.log('TypeError:', e.message);
}

// ReferenceError
try {
  console.log(undefinedVariable);
} catch (e) {
  console.log('ReferenceError:', e.message);
}

// RangeError
try {
  const arr = new Array(-1);
} catch (e) {
  console.log('RangeError:', e.message);
}

// SyntaxError (via eval or JSON.parse)
try {
  JSON.parse('{invalid json}');
} catch (e) {
  console.log('SyntaxError:', e.message);
}

// URIError
try {
  decodeURIComponent('%');
} catch (e) {
  console.log('URIError:', e.message);
}

// ============================================
// ERROR OBJECT PROPERTIES
// ============================================

console.log('\n=== Error Properties ===');

try {
  throw new Error('Test error');
} catch (error) {
  console.log('Name:', error.name);
  console.log('Message:', error.message);
  console.log('Stack:', error.stack.split('\n')[0]);
}

// ============================================
// AGGREGATE ERROR (ES2021)
// ============================================

console.log('\n=== AggregateError ===');

try {
  throw new AggregateError(
    [
      new Error('First error'),
      new TypeError('Second error'),
      new RangeError('Third error'),
    ],
    'Multiple errors occurred'
  );
} catch (e) {
  console.log('Message:', e.message);
  console.log('Error count:', e.errors.length);
  e.errors.forEach((err, i) => {
    console.log(`  Error ${i + 1}: ${err.name} - ${err.message}`);
  });
}

// ============================================
// BASIC CUSTOM ERROR
// ============================================

console.log('\n=== Basic Custom Error ===');

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

try {
  throw new ValidationError('Email format is invalid');
} catch (e) {
  console.log(`${e.name}: ${e.message}`);
  console.log('Is ValidationError:', e instanceof ValidationError);
  console.log('Is Error:', e instanceof Error);
}

// ============================================
// CUSTOM ERROR WITH PROPERTIES
// ============================================

console.log('\n=== Custom Error with Properties ===');

class HttpError extends Error {
  constructor(statusCode, message, body = null) {
    super(message);
    this.name = 'HttpError';
    this.statusCode = statusCode;
    this.body = body;

    if (Error.captureStackTrace) {
      Error.captureStackTrace(this, HttpError);
    }
  }

  get isClientError() {
    return this.statusCode >= 400 && this.statusCode < 500;
  }

  get isServerError() {
    return this.statusCode >= 500;
  }

  toString() {
    return `${this.name} [${this.statusCode}]: ${this.message}`;
  }
}

try {
  throw new HttpError(404, 'Resource not found', { id: 123 });
} catch (e) {
  console.log(e.toString());
  console.log('Status:', e.statusCode);
  console.log('Body:', e.body);
  console.log('Is client error:', e.isClientError);
  console.log('Is server error:', e.isServerError);
}

// ============================================
// ERROR HIERARCHY
// ============================================

console.log('\n=== Error Hierarchy ===');

// Base application error
class AppError extends Error {
  constructor(message, code) {
    super(message);
    this.name = this.constructor.name;
    this.code = code;
    this.timestamp = new Date();
  }

  toJSON() {
    return {
      name: this.name,
      message: this.message,
      code: this.code,
      timestamp: this.timestamp.toISOString(),
    };
  }
}

class NotFoundError extends AppError {
  constructor(resource, id) {
    super(`${resource} with id ${id} not found`, 'NOT_FOUND');
    this.resource = resource;
    this.resourceId = id;
  }
}

class ValidationError2 extends AppError {
  constructor(field, message) {
    super(message, 'VALIDATION_ERROR');
    this.field = field;
  }
}

class AuthError extends AppError {
  constructor(message = 'Authentication required') {
    super(message, 'AUTH_ERROR');
  }
}

// Test hierarchy
const errors = [
  new NotFoundError('User', 42),
  new ValidationError2('email', 'Invalid email format'),
  new AuthError(),
];

errors.forEach((err) => {
  console.log('\n' + err.name + ':');
  console.log('  Message:', err.message);
  console.log('  Code:', err.code);
  console.log('  JSON:', JSON.stringify(err.toJSON()));
});

// ============================================
// ERROR TYPE CHECKING
// ============================================

console.log('\n=== Error Type Checking ===');

function handleError(error) {
  // Using instanceof
  if (error instanceof NotFoundError) {
    console.log(`Resource ${error.resource} not found`);
  } else if (error instanceof ValidationError2) {
    console.log(`Validation failed for field: ${error.field}`);
  } else if (error instanceof AuthError) {
    console.log('Authentication required');
  } else if (error instanceof AppError) {
    console.log(`App error [${error.code}]: ${error.message}`);
  } else {
    console.log('Unknown error:', error.message);
  }
}

handleError(new NotFoundError('Product', 99));
handleError(new ValidationError2('username', 'Too short'));
handleError(new AuthError('Token expired'));

// ============================================
// ERROR WRAPPING
// ============================================

console.log('\n=== Error Wrapping ===');

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

function simulateDatabaseQuery() {
  try {
    // Simulate a low-level error
    throw new Error('Connection timeout');
  } catch (error) {
    throw new DatabaseError('Failed to execute query', error);
  }
}

try {
  simulateDatabaseQuery();
} catch (error) {
  console.log('Error:', error.message);
  console.log('Original error:', error.originalError.message);
}

// ============================================
// ERROR CAUSE (ES2022)
// ============================================

console.log('\n=== Error Cause (ES2022) ===');

function loadConfig() {
  try {
    throw new Error('File not found: config.json');
  } catch (error) {
    throw new Error('Failed to load configuration', { cause: error });
  }
}

function initializeApp() {
  try {
    loadConfig();
  } catch (error) {
    throw new Error('App initialization failed', { cause: error });
  }
}

try {
  initializeApp();
} catch (error) {
  console.log('Error:', error.message);
  console.log('Cause:', error.cause?.message);
  console.log('Root cause:', error.cause?.cause?.message);
}

// ============================================
// PRACTICAL: FORM VALIDATION ERRORS
// ============================================

console.log('\n=== Form Validation Example ===');

class FormValidationError extends Error {
  constructor(errors) {
    super('Form validation failed');
    this.name = 'FormValidationError';
    this.errors = errors; // { fieldName: [messages] }
  }

  get fieldErrors() {
    return Object.entries(this.errors).map(([field, messages]) => ({
      field,
      messages,
    }));
  }

  hasErrors(field) {
    return field in this.errors;
  }
}

function validateUser(data) {
  const errors = {};

  if (!data.email || !data.email.includes('@')) {
    errors.email = ['Invalid email format'];
  }

  if (!data.password || data.password.length < 8) {
    errors.password = ['Password must be at least 8 characters'];
  }

  if (!data.age || data.age < 18) {
    errors.age = ['Must be 18 or older'];
  }

  if (Object.keys(errors).length > 0) {
    throw new FormValidationError(errors);
  }

  return true;
}

try {
  validateUser({ email: 'invalid', password: '123', age: 16 });
} catch (e) {
  if (e instanceof FormValidationError) {
    console.log('Validation errors:');
    e.fieldErrors.forEach(({ field, messages }) => {
      console.log(`  ${field}: ${messages.join(', ')}`);
    });
  }
}

// ============================================
// PRACTICAL: API ERROR HANDLING
// ============================================

console.log('\n=== API Error Handling ===');

class APIError extends Error {
  constructor(response) {
    super(response.message || 'API request failed');
    this.name = 'APIError';
    this.status = response.status;
    this.code = response.code;
    this.details = response.details;
  }

  static fromResponse(status, body) {
    return new APIError({
      status,
      message: body.error?.message || 'Unknown error',
      code: body.error?.code,
      details: body.error?.details,
    });
  }
}

async function fetchAPI(url) {
  // Simulate API response
  const response = {
    status: 422,
    body: {
      error: {
        code: 'INVALID_INPUT',
        message: 'Invalid request parameters',
        details: { field: 'email', reason: 'already exists' },
      },
    },
  };

  if (response.status >= 400) {
    throw APIError.fromResponse(response.status, response.body);
  }

  return response.body;
}

(async () => {
  try {
    await fetchAPI('/api/users');
  } catch (e) {
    if (e instanceof APIError) {
      console.log(`API Error [${e.status}] ${e.code}: ${e.message}`);
      console.log('Details:', e.details);
    }
  }
})();

console.log('\n=== Examples Complete ===');
Examples - JavaScript Tutorial | DeepML