javascript
exercises
exercises.js⚡javascript
/**
* 4.7 Error Types and Custom Errors - Exercises
*/
/**
* Exercise 1: Error Type Identification
* Create a function that catches an error and identifies its type
*/
function identifyError(fn) {
try {
fn();
return 'No error';
} catch (error) {
// TODO: Return the specific error type as a string
// Handle: TypeError, ReferenceError, RangeError, SyntaxError, URIError, Error
// Your code here
}
}
// Test cases:
// console.log(identifyError(() => null.property)); // "TypeError"
// console.log(identifyError(() => nonExistent)); // "ReferenceError"
// console.log(identifyError(() => new Array(-1))); // "RangeError"
// console.log(identifyError(() => JSON.parse('{'))); // "SyntaxError"
/**
* Exercise 2: Basic Custom Error
* Create a custom error for invalid age
*/
class InvalidAgeError extends Error {
// TODO: Implement this class
// - Constructor takes age and message
// - Store the age value
// - Set appropriate name
}
function validateAge(age) {
// TODO: Throw InvalidAgeError if age < 0 or age > 150
// Return true if valid
}
// Test cases:
// try {
// validateAge(-5);
// } catch (e) {
// console.log(e.name); // "InvalidAgeError"
// console.log(e.age); // -5
// console.log(e.message); // "Age cannot be negative"
// }
/**
* Exercise 3: Error Hierarchy
* Create a payment error hierarchy
*/
// TODO: Create base PaymentError class with:
// - message, code, timestamp properties
// - toJSON() method
// TODO: Create InsufficientFundsError extending PaymentError
// - Takes amount and balance
// - Code should be 'INSUFFICIENT_FUNDS'
// TODO: Create InvalidCardError extending PaymentError
// - Takes cardNumber (last 4 digits only)
// - Code should be 'INVALID_CARD'
// TODO: Create PaymentDeclinedError extending PaymentError
// - Takes reason
// - Code should be 'PAYMENT_DECLINED'
/**
* Exercise 4: Error with Cause Chain
* Implement functions that chain error causes
*/
function parseJSON(jsonString) {
// TODO: Parse JSON and wrap any errors with cause
// Throw new Error with message "JSON parsing failed" and cause
}
function loadData(jsonString) {
// TODO: Call parseJSON and wrap any errors
// Throw new Error with message "Failed to load data" and cause
}
function processData(jsonString) {
// TODO: Call loadData and wrap any errors
// Throw new Error with message "Data processing failed" and cause
}
// Implement a function to get the full error chain
function getErrorChain(error) {
// TODO: Return array of all error messages in the chain
// Example: ["Data processing failed", "Failed to load data", "JSON parsing failed", "..."]
}
// Test:
// try {
// processData('{invalid}');
// } catch (e) {
// console.log(getErrorChain(e));
// }
/**
* Exercise 5: Form Validation Error
* Create a comprehensive form validation error class
*/
class FieldValidationError {
// TODO: Implement
// - field: string
// - value: any
// - rules: string[] (failed validation rules)
}
class FormValidationError extends Error {
// TODO: Implement
// - fieldErrors: FieldValidationError[]
// - getErrorsForField(field): string[]
// - hasError(field): boolean
// - getAllErrors(): object with all fields and their errors
}
// Create a validator function
function validateRegistrationForm(data) {
// TODO: Validate:
// - username: required, min 3 chars, max 20 chars
// - email: required, must contain @
// - password: required, min 8 chars, must contain number
// - confirmPassword: must match password
// Throw FormValidationError if any validation fails
}
// Test:
// try {
// validateRegistrationForm({
// username: 'ab',
// email: 'invalid',
// password: 'short',
// confirmPassword: 'different'
// });
// } catch (e) {
// console.log(e.getAllErrors());
// }
/**
* Exercise 6: HTTP Error Handler
* Create a robust HTTP error handling system
*/
// TODO: Create these HTTP error classes:
// - BadRequestError (400)
// - UnauthorizedError (401)
// - ForbiddenError (403)
// - NotFoundError (404)
// - InternalServerError (500)
// All should extend a base HttpError class with:
// - statusCode, statusText, body, headers properties
// - toResponse() method returning { status, statusText, body }
// Create a factory function
function createHttpError(statusCode, message, details = {}) {
// TODO: Return appropriate error class based on statusCode
}
// Create an error handler
function handleHttpError(error) {
// TODO: Return standardized error response object
// { success: false, error: { code, message, details } }
}
/**
* Exercise 7: Async Operation Error
* Handle errors in async operations with retries
*/
class RetryableError extends Error {
// TODO: Implement
// - retryable: boolean
// - retryAfter: number (seconds)
// - attempts: number
}
async function withRetry(fn, maxAttempts = 3, delay = 1000) {
// TODO: Implement retry logic
// - Call fn()
// - If it throws RetryableError and retryable is true, retry
// - Wait 'delay' ms between retries (use delay * attempt for exponential backoff)
// - After maxAttempts, throw the last error with all attempts info
}
// Test:
// let attempts = 0;
// await withRetry(async () => {
// attempts++;
// if (attempts < 3) {
// const err = new RetryableError('Temporary failure');
// err.retryable = true;
// throw err;
// }
// return 'Success!';
// });
/**
* Exercise 8: Error Aggregation
* Collect multiple errors and report them together
*/
class BulkOperationError extends Error {
// TODO: Implement
// - successCount: number
// - failureCount: number
// - errors: array of { index, error, item }
// - getFailedItems(): array of items that failed
// - getSummary(): string summary of operation
}
async function processBulk(items, processor) {
// TODO: Process all items, collecting errors
// - Don't stop on individual errors
// - At the end, if any errors occurred, throw BulkOperationError
// - Return array of successful results
}
// Test:
// const items = [1, 2, 'invalid', 4, null, 6];
// try {
// await processBulk(items, async (item) => {
// if (typeof item !== 'number') {
// throw new Error(`Invalid item: ${item}`);
// }
// return item * 2;
// });
// } catch (e) {
// console.log(e.getSummary());
// console.log(e.getFailedItems());
// }
/**
* Exercise 9: Error Serialization
* Create errors that serialize properly for logging/API responses
*/
class SerializableError extends Error {
// TODO: Implement
// - toJSON(): return full error info as plain object
// - toString(): return formatted string
// - static fromJSON(json): recreate error from JSON
}
// Create specific serializable errors
class DatabaseError extends SerializableError {
// TODO: Include query, params, connection info
}
class ExternalServiceError extends SerializableError {
// TODO: Include service name, endpoint, response time
}
/**
* Exercise 10: Error Factory with Context
* Create a factory that adds context to errors
*/
function createErrorFactory(context) {
// TODO: Return an object with methods to create contextualized errors
// - validation(field, message): create validation error with context
// - notFound(resource, id): create not found error with context
// - permission(action, resource): create permission error with context
// - internal(message, cause): create internal error with context
// Each error should include:
// - The passed context (userId, requestId, etc.)
// - Timestamp
// - Stack trace
}
// Usage:
// const errors = createErrorFactory({
// userId: 'user123',
// requestId: 'req-456',
// service: 'user-service'
// });
//
// throw errors.notFound('User', 42);
// throw errors.validation('email', 'Invalid format');
/**
* BONUS CHALLENGES
*/
/**
* Bonus 1: Error Boundary Pattern
* Implement a class that acts as an error boundary
*/
class ErrorBoundary {
// TODO: Implement
// - onError callback for handling errors
// - wrap(fn) method that catches errors and calls onError
// - wrapAsync(fn) for async functions
// - recover(fn) method to provide fallback value on error
}
/**
* Bonus 2: Type-Safe Error Codes
* Create a system with predefined error codes
*/
const ErrorCodes = {
// TODO: Define error codes with metadata
// E001: { message: '...', severity: '...', retryable: boolean }
};
class CodedError extends Error {
// TODO: Create error from ErrorCodes
// constructor(code, details?)
}
// ============================================
// SOLUTION KEY (for reference)
// ============================================
/*
// Exercise 1 Solution:
function identifyError(fn) {
try {
fn();
return 'No error';
} catch (error) {
if (error instanceof TypeError) return 'TypeError';
if (error instanceof ReferenceError) return 'ReferenceError';
if (error instanceof RangeError) return 'RangeError';
if (error instanceof SyntaxError) return 'SyntaxError';
if (error instanceof URIError) return 'URIError';
return 'Error';
}
}
// Exercise 2 Solution:
class InvalidAgeError extends Error {
constructor(age, message) {
super(message || `Invalid age: ${age}`);
this.name = 'InvalidAgeError';
this.age = age;
}
}
function validateAge(age) {
if (age < 0) {
throw new InvalidAgeError(age, 'Age cannot be negative');
}
if (age > 150) {
throw new InvalidAgeError(age, 'Age cannot exceed 150');
}
return true;
}
// Exercise 3 Solution:
class PaymentError extends Error {
constructor(message, code) {
super(message);
this.name = 'PaymentError';
this.code = code;
this.timestamp = new Date();
}
toJSON() {
return {
name: this.name,
message: this.message,
code: this.code,
timestamp: this.timestamp.toISOString()
};
}
}
class InsufficientFundsError extends PaymentError {
constructor(amount, balance) {
super(`Insufficient funds: need ${amount}, have ${balance}`, 'INSUFFICIENT_FUNDS');
this.amount = amount;
this.balance = balance;
}
}
class InvalidCardError extends PaymentError {
constructor(cardNumber) {
const last4 = String(cardNumber).slice(-4);
super(`Invalid card ending in ${last4}`, 'INVALID_CARD');
this.last4 = last4;
}
}
class PaymentDeclinedError extends PaymentError {
constructor(reason) {
super(`Payment declined: ${reason}`, 'PAYMENT_DECLINED');
this.reason = reason;
}
}
// Exercise 4 Solution:
function parseJSON(jsonString) {
try {
return JSON.parse(jsonString);
} catch (error) {
throw new Error('JSON parsing failed', { cause: error });
}
}
function loadData(jsonString) {
try {
return parseJSON(jsonString);
} catch (error) {
throw new Error('Failed to load data', { cause: error });
}
}
function processData(jsonString) {
try {
return loadData(jsonString);
} catch (error) {
throw new Error('Data processing failed', { cause: error });
}
}
function getErrorChain(error) {
const chain = [];
let current = error;
while (current) {
chain.push(current.message);
current = current.cause;
}
return chain;
}
*/
console.log('Complete the exercises above!');
console.log('Check the solution key at the bottom for guidance.');