Exemples de Gestion des Erreurs Web TypeScript

Exemples de gestion des erreurs Web TypeScript incluant la capture d'exceptions, la journalisation et la validation des paramètres

Key Facts

Category
TypeScript
Items
3
Format Families
audio

Sample Overview

Exemples de gestion des erreurs Web TypeScript incluant la capture d'exceptions, la journalisation et la validation des paramètres This sample set belongs to TypeScript and can be used to test related workflows inside Elysia Tools.

💻 Capture d'Exceptions typescript

🟢 simple ⭐⭐⭐

Gérer les exceptions try-catch, classes d'erreur personnalisées et propagation des erreurs

⏱️ 20 min 🏷️ typescript, web, error handling
Prerequisites: Basic TypeScript, Error handling
// Web TypeScript Exception Catching Examples
// Try-catch blocks, custom errors, and error handling patterns

// 1. Basic Try-Catch Handler
class TryCatchHandler {
  // Safe execute with default value
  safeExecute<T>(fn: () => T, defaultValue: T): T {
    try {
      return fn();
    } catch (error) {
      console.error('Error in safeExecute:', error);
      return defaultValue;
    }
  }

  // Safe async execute
  async safeExecuteAsync<T>(fn: () => Promise<T>, defaultValue: T): Promise<T> {
    try {
      return await fn();
    } catch (error) {
      console.error('Error in safeExecuteAsync:', error);
      return defaultValue;
    }
  }

  // Execute with error callback
  executeWithCallback<T>(
    fn: () => T,
    onError: (error: unknown) => void
  ): T | null {
    try {
      return fn();
    } catch (error) {
      onError(error);
      return null;
    }
  }

  // Multiple try attempts
  async tryMultipleTimes<T>(
    fn: () => Promise<T>,
    attempts: number = 3,
    delay: number = 1000
  ): Promise<T> {
    for (let i = 0; i < attempts; i++) {
      try {
        return await fn();
      } catch (error) {
        if (i === attempts - 1) {
          throw error;
        }
        console.log(`Attempt ${i + 1} failed, retrying...`);
        await this.sleep(delay);
      }
    }
    throw new Error('All attempts failed');
  }

  private sleep(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
}

// 2. Custom Error Classes
class CustomError extends Error {
  constructor(
    message: string,
    public code: string,
    public statusCode: number = 500,
    public details?: any
  ) {
    super(message);
    this.name = this.constructor.name;
    Error.captureStackTrace?.(this, this.constructor);
  }
}

class ValidationError extends CustomError {
  constructor(message: string, details?: any) {
    super(message, 'VALIDATION_ERROR', 400, details);
  }
}

class AuthenticationError extends CustomError {
  constructor(message: string) {
    super(message, 'AUTH_ERROR', 401);
  }
}

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

class NetworkError extends CustomError {
  constructor(message: string, public originalError?: Error) {
    super(message, 'NETWORK_ERROR', 0);
  }
}

class PermissionError extends CustomError {
  constructor(resource: string, action: string) {
    super(
      `Permission denied to ${action} ${resource}`,
      'PERMISSION_ERROR',
      403
    );
  }
}

// 3. Error Boundary Pattern
class ErrorBoundary {
  private errorHandler?: (error: Error) => void;

  constructor(errorHandler?: (error: Error) => void) {
    this.errorHandler = errorHandler;
  }

  // Wrap function with error boundary
  wrap<T extends (...args: any[]) => any>(fn: T): T {
    return ((...args: any[]) => {
      try {
        return fn(...args);
      } catch (error) {
        this.handleError(error as Error);
        return null;
      }
    }) as T;
  }

  // Wrap async function
  wrapAsync<T extends (...args: any[]) => Promise<any>>(fn: T): T {
    return (async (...args: any[]) => {
      try {
        return await fn(...args);
      } catch (error) {
        this.handleError(error as Error);
        return null;
      }
    }) as T;
  }

  // Handle error
  private handleError(error: Error): void {
    if (this.errorHandler) {
      this.errorHandler(error);
    } else {
      console.error('ErrorBoundary caught error:', error);
    }
  }
}

// 4. Error Handler Chain
class ErrorHandlerChain {
  private handlers: Array<(error: Error) => boolean> = [];

  // Add handler to chain
  addHandler(handler: (error: Error) => boolean): this {
    this.handlers.push(handler);
    return this;
  }

  // Handle error through chain
  handle(error: Error): void {
    for (const handler of this.handlers) {
      const handled = handler(error);
      if (handled) {
        return;
      }
    }

    // No handler handled the error
    console.error('Unhandled error:', error);
  }

  // Create common handler chain
  static createDefault(): ErrorHandlerChain {
    return new ErrorHandlerChain()
      .addHandler((error) => {
        if (error instanceof ValidationError) {
          console.warn('Validation error:', error.message);
          return true;
        }
        return false;
      })
      .addHandler((error) => {
        if (error instanceof AuthenticationError) {
          console.warn('Authentication error:', error.message);
          // Redirect to login
          return true;
        }
        return false;
      })
      .addHandler((error) => {
        if (error instanceof NotFoundError) {
          console.warn('Not found:', error.message);
          return true;
        }
        return false;
      })
      .addHandler((error) => {
        if (error instanceof NetworkError) {
          console.error('Network error:', error.message);
          return true;
        }
        return false;
      });
  }
}

// 5. Error Recovery Manager
class ErrorRecoveryManager {
  // Execute with fallback
  async withFallback<T>(
    primaryFn: () => Promise<T>,
    fallbackFn: () => Promise<T>
  ): Promise<T> {
    try {
      return await primaryFn();
    } catch (primaryError) {
      console.warn('Primary function failed, using fallback:', primaryError);
      try {
        return await fallbackFn();
      } catch (fallbackError) {
        console.error('Fallback function also failed:', fallbackError);
        throw new Error('Both primary and fallback functions failed');
      }
    }
  }

  // Execute with multiple fallbacks
  async withMultipleFallbacks<T>(
    ...fns: Array<() => Promise<T>>
  ): Promise<T> {
    for (let i = 0; i < fns.length; i++) {
      try {
        return await fns[i]();
      } catch (error) {
        console.warn(`Function ${i + 1} failed:`, error);
        if (i === fns.length - 1) {
          throw error;
        }
      }
    }
    throw new Error('All functions failed');
  }

  // Execute with circuit breaker
  withCircuitBreaker<T>(
    fn: () => Promise<T>,
    options: {
      failureThreshold?: number;
      recoveryTimeout?: number;
    } = {}
  ): () => Promise<T> {
    const {
      failureThreshold = 5,
      recoveryTimeout = 60000
    } = options;

    let failures = 0;
    let lastFailureTime = 0;
    let circuitOpen = false;

    return async () => {
      if (circuitOpen) {
        const timeSinceLastFailure = Date.now() - lastFailureTime;

        if (timeSinceLastFailure < recoveryTimeout) {
          throw new Error('Circuit breaker is open');
        } else {
          circuitOpen = false;
          failures = 0;
        }
      }

      try {
        const result = await fn();
        failures = 0;
        return result;
      } catch (error) {
        failures++;
        lastFailureTime = Date.now();

        if (failures >= failureThreshold) {
          circuitOpen = true;
          console.error('Circuit breaker opened after', failures, 'failures');
        }

        throw error;
      }
    };
  }
}

// 6. Error Context Manager
class ErrorContext {
  private context: Map<string, any> = new Map();

  // Set context value
  set(key: string, value: any): void {
    this.context.set(key, value);
  }

  // Get context value
  get(key: string): any {
    return this.context.get(key);
  }

  // Create error with context
  createError(message: string, errorClass?: new (...args: any[]) => Error): Error {
    const error = errorClass
      ? new errorClass(message)
      : new Error(message);

    (error as any).context = Object.fromEntries(this.context);

    return error;
  }

  // Wrap function with context
  withContext<T extends (...args: any[]) => any>(
    fn: T,
    contextData: Record<string, any>
  ): T {
    return ((...args: any[]) => {
      // Add context
      for (const key in contextData) {
        this.set(key, contextData[key]);
      }

      try {
        return fn(...args);
      } catch (error) {
        // Attach context to error
        (error as any).context = Object.fromEntries(this.context);
        throw error;
      }
    }) as T;
  }

  // Clear context
  clear(): void {
    this.context.clear();
  }
}

// 7. Global Error Handler
class GlobalErrorHandler {
  private handlers: Array<(error: ErrorEvent) => void> = [];

  // Setup global error handlers
  setup(): void {
    window.addEventListener('error', (event) => {
      this.handleError(new Error(event.message), event);
    });

    window.addEventListener('unhandledrejection', (event) => {
      this.handleError(event.reason, event);
    });
  }

  // Add custom handler
  addHandler(handler: (error: ErrorEvent) => void): void {
    this.handlers.push(handler);
  }

  // Handle error
  private handleError(error: Error, originalEvent?: any): void {
    console.error('Global error handler caught:', error);

    // Call custom handlers
    for (const handler of this.handlers) {
      try {
        handler({ error } as ErrorEvent);
      } catch (handlerError) {
        console.error('Error in error handler:', handlerError);
      }
    }
  }
}

// Usage Examples
async function demonstrateExceptionCatching() {
  console.log('=== Web TypeScript Exception Catching Examples ===\n');

  const tryCatch = new TryCatchHandler();
  const errorBoundary = new ErrorBoundary();
  const handlerChain = ErrorHandlerChain.createDefault();
  const recoveryManager = new ErrorRecoveryManager();
  const errorContext = new ErrorContext();

  // 1. Basic try-catch
  console.log('--- 1. Basic Try-Catch ---');
  const result = tryCatch.safeExecute(
    () => {
      console.log('Executing risky operation...');
      if (Math.random() > 0.5) {
        throw new Error('Random error occurred!');
      }
      return 'Success';
    },
    'Default Value'
  );
  console.log(`Result: ${result}`);

  // 2. Custom errors
  console.log('\n--- 2. Custom Errors ---');
  try {
    throw new ValidationError('Invalid input', { field: 'email', value: 'invalid' });
  } catch (error) {
    if (error instanceof ValidationError) {
      console.log(`Validation error: ${error.message}`);
      console.log(`Code: ${error.code}`);
      console.log(`Details: ${JSON.stringify(error.details)}`);
    }
  }

  // 3. Error boundary
  console.log('\n--- 3. Error Boundary ---');
  const safeFunction = errorBoundary.wrap(() => {
    throw new Error('This error will be caught');
  });
  safeFunction();

  // 4. Error handler chain
  console.log('\n--- 4. Error Handler Chain ---');
  handlerChain.handle(new ValidationError('Validation failed'));
  handlerChain.handle(new NotFoundError('User', '123'));

  // 5. Error recovery
  console.log('\n--- 5. Error Recovery ---');
  const recoveredResult = await recoveryManager.withFallback(
    async () => {
      throw new Error('Primary failed');
    },
    async () => {
      console.log('Using fallback...');
      return 'Fallback Result';
    }
  );
  console.log(`Recovered result: ${recoveredResult}`);

  // 6. Error context
  console.log('\n--- 6. Error Context ---');
  const contextualFn = errorContext.withContext(
    () => {
      throw new Error('Error with context');
    },
    { userId: '123', action: 'delete' }
  );

  try {
    contextualFn();
  } catch (error: any) {
    console.log(`Error context: ${JSON.stringify((error as any).context)}`);
  }

  console.log('\n=== All Exception Catching Examples Completed ===');
}

// Export functions
export { TryCatchHandler, CustomError, ValidationError, AuthenticationError, NotFoundError, NetworkError, PermissionError, ErrorBoundary, ErrorHandlerChain, ErrorRecoveryManager, ErrorContext, GlobalErrorHandler };
export { demonstrateExceptionCatching };

💻 Journalisation typescript

🟡 intermediate ⭐⭐⭐

Implémenter des systèmes de journalisation avec plusieurs niveaux, sorties et formateurs

⏱️ 25 min 🏷️ typescript, web, error handling, logging
Prerequisites: Intermediate TypeScript, IndexedDB
// Web TypeScript Log Recording Examples
// Comprehensive logging system with multiple levels and outputs

// 1. Log Level Enum
enum LogLevel {
  DEBUG = 0,
  INFO = 1,
  WARN = 2,
  ERROR = 3,
  FATAL = 4
}

// 2. Log Entry Interface
interface LogEntry {
  timestamp: Date;
  level: LogLevel;
  message: string;
  context?: Record<string, any>;
  error?: Error;
  stack?: string;
}

// 3. Basic Logger
class Logger {
  private minLevel: LogLevel = LogLevel.INFO;
  private outputs: LogOutput[] = [];

  // Set minimum log level
  setMinLevel(level: LogLevel): void {
    this.minLevel = level;
  }

  // Add log output
  addOutput(output: LogOutput): void {
    this.outputs.push(output);
  }

  // Remove log output
  removeOutput(output: LogOutput): void {
    const index = this.outputs.indexOf(output);
    if (index > -1) {
      this.outputs.splice(index, 1);
    }
  }

  // Log at debug level
  debug(message: string, context?: Record<string, any>): void {
    this.log(LogLevel.DEBUG, message, context);
  }

  // Log at info level
  info(message: string, context?: Record<string, any>): void {
    this.log(LogLevel.INFO, message, context);
  }

  // Log at warn level
  warn(message: string, context?: Record<string, any>): void {
    this.log(LogLevel.WARN, message, context);
  }

  // Log at error level
  error(message: string, error?: Error, context?: Record<string, any>): void {
    this.log(LogLevel.ERROR, message, context, error);
  }

  // Log at fatal level
  fatal(message: string, error?: Error, context?: Record<string, any>): void {
    this.log(LogLevel.FATAL, message, context, error);
  }

  // Internal log method
  private log(
    level: LogLevel,
    message: string,
    context?: Record<string, any>,
    error?: Error
  ): void {
    if (level < this.minLevel) {
      return;
    }

    const entry: LogEntry = {
      timestamp: new Date(),
      level,
      message,
      context,
      error
    };

    if (error?.stack) {
      entry.stack = error.stack;
    }

    for (const output of this.outputs) {
      output.write(entry);
    }
  }

  // Create child logger with additional context
  child(context: Record<string, any>): ChildLogger {
    return new ChildLogger(this, context);
  }
}

// 4. Child Logger
class ChildLogger {
  constructor(
    private parent: Logger,
    private context: Record<string, any>
  ) {}

  debug(message: string, additionalContext?: Record<string, any>): void {
    this.parent.debug(
      message,
      additionalContext ? { ...this.context, ...additionalContext } : this.context
    );
  }

  info(message: string, additionalContext?: Record<string, any>): void {
    this.parent.info(
      message,
      additionalContext ? { ...this.context, ...additionalContext } : this.context
    );
  }

  warn(message: string, additionalContext?: Record<string, any>): void {
    this.parent.warn(
      message,
      additionalContext ? { ...this.context, ...additionalContext } : this.context
    );
  }

  error(message: string, error?: Error, additionalContext?: Record<string, any>): void {
    this.parent.error(
      message,
      error,
      additionalContext ? { ...this.context, ...additionalContext } : this.context
    );
  }
}

// 5. Log Output Interface
interface LogOutput {
  write(entry: LogEntry): void;
}

// 6. Console Output
class ConsoleOutput implements LogOutput {
  write(entry: LogEntry): void {
    const levelName = LogLevel[entry.level];
    const prefix = `[${entry.timestamp.toISOString()}] [${levelName}]`;

    switch (entry.level) {
      case LogLevel.DEBUG:
        console.debug(prefix, entry.message, entry.context || '');
        break;
      case LogLevel.INFO:
        console.info(prefix, entry.message, entry.context || '');
        break;
      case LogLevel.WARN:
        console.warn(prefix, entry.message, entry.context || '');
        break;
      case LogLevel.ERROR:
      case LogLevel.FATAL:
        console.error(prefix, entry.message, entry.error || entry.context || '');
        if (entry.stack) {
          console.error(entry.stack);
        }
        break;
    }
  }
}

// 7. File Output (IndexedDB)
class IndexedDBOutput implements LogOutput {
  private dbName: string = 'logs';
  private storeName: string = 'entries';
  private db: IDBDatabase | null = null;
  private buffer: LogEntry[] = [];
  private bufferSize: number = 100;

  async initialize(): Promise<void> {
    return new Promise((resolve, reject) => {
      const request = indexedDB.open(this.dbName, 1);

      request.onerror = () => reject(request.error);
      request.onsuccess = () => {
        this.db = request.result;
        resolve();
      };

      request.onupgradeneeded = (event) => {
        const db = (event.target as IDBOpenDBRequest).result;
        if (!db.objectStoreNames.contains(this.storeName)) {
          const store = db.createObjectStore(this.storeName, { keyPath: 'id', autoIncrement: true });
          store.createIndex('timestamp', 'timestamp', { unique: false });
          store.createIndex('level', 'level', { unique: false });
        }
      };
    });
  }

  write(entry: LogEntry): void {
    this.buffer.push(entry);

    if (this.buffer.length >= this.bufferSize) {
      this.flush();
    }
  }

  flush(): void {
    if (this.buffer.length === 0 || !this.db) {
      return;
    }

    const transaction = this.db.transaction([this.storeName], 'readwrite');
    const store = transaction.objectStore(this.storeName);

    for (const entry of this.buffer) {
      store.add(entry);
    }

    this.buffer = [];
  }

  // Query logs
  async queryLogs(
    filter: {
      startDate?: Date;
      endDate?: Date;
      minLevel?: LogLevel;
      limit?: number;
    } = {}
  ): Promise<LogEntry[]> {
    if (!this.db) {
      await this.initialize();
    }

    return new Promise((resolve, reject) => {
      const transaction = this.db!.transaction([this.storeName], 'readonly');
      const store = transaction.objectStore(this.storeName);
      const index = store.index('timestamp');

      const range = IDBKeyRange.lowerBound(filter.startDate?.getTime() || 0);
      const request = index.openCursor(range);

      const results: LogEntry[] = [];
      let count = 0;

      request.onsuccess = (event) => {
        const cursor = (event.target as IDBRequest).result;

        if (cursor && (!filter.limit || count < filter.limit)) {
          const entry = cursor.value as LogEntry;

          if (!filter.minLevel || entry.level >= filter.minLevel) {
            if (!filter.endDate || entry.timestamp <= filter.endDate) {
              results.push(entry);
              count++;
            }
          }

          cursor.continue();
        } else {
          resolve(results);
        }
      };

      request.onerror = () => reject(request.error);
    });
  }

  // Clear old logs
  async clearOldLogs(beforeDate: Date): Promise<number> {
    if (!this.db) {
      await this.initialize();
    }

    return new Promise((resolve, reject) => {
      const transaction = this.db!.transaction([this.storeName], 'readwrite');
      const store = transaction.objectStore(this.storeName);
      const index = store.index('timestamp');

      const range = IDBKeyRange.upperBound(beforeDate.getTime());
      const request = index.openCursor(range);

      let count = 0;

      request.onsuccess = (event) => {
        const cursor = (event.target as IDBRequest).result;

        if (cursor) {
          cursor.delete();
          count++;
          cursor.continue();
        } else {
          resolve(count);
        }
      };

      request.onerror = () => reject(request.error);
    });
  }
}

// 8. Remote Output (HTTP)
class RemoteOutput implements LogOutput {
  private endpoint: string;
  private batchSize: number = 10;
  private buffer: LogEntry[] = [];

  constructor(endpoint: string) {
    this.endpoint = endpoint;
  }

  write(entry: LogEntry): void {
    this.buffer.push(entry);

    if (this.buffer.length >= this.batchSize) {
      this.flush();
    }
  }

  async flush(): Promise<void> {
    if (this.buffer.length === 0) {
      return;
    }

    const entries = [...this.buffer];
    this.buffer = [];

    try {
      const response = await fetch(this.endpoint, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(entries)
      });

      if (!response.ok) {
        console.error('Failed to send logs to remote endpoint');
      }
    } catch (error) {
      console.error('Error sending logs to remote endpoint:', error);
    }
  }
}

// 9. Formatted Output
class FormattedOutput implements LogOutput {
  constructor(
    private output: LogOutput,
    private formatter: (entry: LogEntry) => string
  ) {}

  write(entry: LogEntry): void {
    const formatted = this.formatter(entry);
    const formattedEntry: LogEntry = {
      ...entry,
      message: formatted
    };
    this.output.write(formattedEntry);
  }
}

// 10. Logger Factory
class LoggerFactory {
  private static instance: Logger;

  static getLogger(): Logger {
    if (!this.instance) {
      this.instance = new Logger();

      // Add default outputs
      this.instance.addOutput(new ConsoleOutput());
    }

    return this.instance;
  }

  static createLogger(name: string): ChildLogger {
    return this.getLogger().child({ logger: name });
  }
}

// Usage Examples
async function demonstrateLogRecording() {
  console.log('=== Web TypeScript Log Recording Examples ===\n');

  const logger = LoggerFactory.getLogger();
  const consoleOutput = new ConsoleOutput();

  // 1. Basic logging
  console.log('--- 1. Basic Logging ---');
  logger.debug('Debug message');
  logger.info('Info message');
  logger.warn('Warning message');
  logger.error('Error message');

  // 2. Logging with context
  console.log('\n--- 2. Logging with Context ---');
  logger.info('User logged in', { userId: '123', ip: '192.168.1.1' });
  logger.warn('High memory usage', { used: 512, total: 1024, unit: 'MB' });

  // 3. Child logger
  console.log('\n--- 3. Child Logger ---');
  const userLogger = logger.child({ component: 'UserService' });
  userLogger.info('Creating user', { username: 'testuser' });
  userLogger.error('User creation failed', new Error('Duplicate email'));

  // 4. IndexedDB output
  console.log('\n--- 4. IndexedDB Output ---');
  const indexedDBOutput = new IndexedDBOutput();
  await indexedDBOutput.initialize();
  logger.addOutput(indexedDBOutput);

  logger.info('This will be saved to IndexedDB');
  logger.warn('This too');

  await indexedDBOutput.flush();

  const logs = await indexedDBOutput.queryLogs({ limit: 10 });
  console.log(`Retrieved ${logs.length} logs from IndexedDB`);

  // 5. Formatted output
  console.log('\n--- 5. Formatted Output ---');
  const jsonOutput = new FormattedOutput(consoleOutput, (entry) => {
    return JSON.stringify({
      timestamp: entry.timestamp,
      level: LogLevel[entry.level],
      message: entry.message,
      context: entry.context
    });
  });

  logger.info('Formatted log entry', { data: 'test' });

  // 6. Remote output (simulated)
  console.log('\n--- 6. Remote Output ---');
  // const remoteOutput = new RemoteOutput('https://api.example.com/logs');
  // logger.addOutput(remoteOutput);

  console.log('\n=== All Log Recording Examples Completed ===');
}

// Export functions
export { LogLevel, Logger, ChildLogger, LogOutput, ConsoleOutput, IndexedDBOutput, RemoteOutput, FormattedOutput, LoggerFactory };
export { demonstrateLogRecording };
export type { LogEntry };

💻 Validation des Paramètres typescript

🟡 intermediate ⭐⭐⭐

Valider les paramètres de fonction avec vérification de type, validation de plage et règles personnalisées

⏱️ 25 min 🏷️ typescript, web, error handling, validation
Prerequisites: Intermediate TypeScript, Decorators
// Web TypeScript Parameter Validation Examples
// Comprehensive parameter validation system

// 1. Validation Result
interface ValidationResult {
  isValid: boolean;
  errors: string[];
}

// 2. Basic Validators
class Validators {
  // Required validator
  static required(value: any): ValidationResult {
    if (value === null || value === undefined || value === '') {
      return {
        isValid: false,
        errors: ['Value is required']
      };
    }
    return { isValid: true, errors: [] };
  }

  // Type validator
  static type(value: any, expectedType: string): ValidationResult {
    const actualType = typeof value;

    if (actualType !== expectedType) {
      return {
        isValid: false,
        errors: [`Expected type '${expectedType}', got '${actualType}'`]
      };
    }

    return { isValid: true, errors: [] };
  }

  // Number range validator
  static range(value: number, min: number, max: number): ValidationResult {
    const errors: string[] = [];

    if (value < min) {
      errors.push(`Value ${value} is less than minimum ${min}`);
    }

    if (value > max) {
      errors.push(`Value ${value} is greater than maximum ${max}`);
    }

    return {
      isValid: errors.length === 0,
      errors
    };
  }

  // String length validator
  static length(value: string, min: number, max: number): ValidationResult {
    const actualLength = value.length;
    const errors: string[] = [];

    if (actualLength < min) {
      errors.push(`Length ${actualLength} is less than minimum ${min}`);
    }

    if (actualLength > max) {
      errors.push(`Length ${actualLength} is greater than maximum ${max}`);
    }

    return {
      isValid: errors.length === 0,
      errors
    };
  }

  // Pattern validator
  static pattern(value: string, regex: RegExp, description?: string): ValidationResult {
    if (!regex.test(value)) {
      return {
        isValid: false,
        errors: [description || `Value does not match pattern ${regex}`]
      };
    }

    return { isValid: true, errors: [] };
  }

  // Email validator
  static email(value: string): ValidationResult {
    const emailRegex = /^[^[\s@]]+@[^\s@]+\.[^\s@]+$/;
    return this.pattern(value, emailRegex, 'Invalid email format');
  }

  // URL validator
  static url(value: string): ValidationResult {
    try {
      new URL(value);
      return { isValid: true, errors: [] };
    } catch {
      return {
        isValid: false,
        errors: ['Invalid URL format']
      };
    }
  }

  // Array validator
  static array(value: any, itemValidator?: (item: any, index: number) => ValidationResult): ValidationResult {
    if (!Array.isArray(value)) {
      return {
        isValid: false,
        errors: ['Value is not an array']
      };
    }

    if (itemValidator) {
      const errors: string[] = [];

      for (let i = 0; i < value.length; i++) {
        const result = itemValidator(value[i], i);
        if (!result.isValid) {
          errors.push(`Index ${i}: ${result.errors.join(', ')}`);
        }
      }

      return {
        isValid: errors.length === 0,
        errors
      };
    }

    return { isValid: true, errors: [] };
  }
}

// 3. Validation Schema
class ValidationSchema {
  private rules: Map<string, Array<(value: any) => ValidationResult>> = new Map();

  // Add rule for field
  addRule(field: string, validator: (value: any) => ValidationResult): this {
    if (!this.rules.has(field)) {
      this.rules.set(field, []);
    }
    this.rules.get(field)!.push(validator);
    return this;
  }

  // Validate object
  validate(obj: any): ValidationResult {
    const allErrors: string[] = [];

    for (const [field, validators] of this.rules) {
      const value = obj[field];

      for (const validator of validators) {
        const result = validator(value);

        if (!result.isValid) {
          allErrors.push(...result.errors.map(err => `${field}: ${err}`));
        }
      }
    }

    return {
      isValid: allErrors.length === 0,
      errors: allErrors
    };
  }

  // Create schema from config
  static fromConfig(config: Record<string, string[]>): ValidationSchema {
    const schema = new ValidationSchema();

    for (const [field, rules] of Object.entries(config)) {
      for (const rule of rules) {
        const validator = Validators.createValidator(rule);
        if (validator) {
          schema.addRule(field, validator);
        }
      }
    }

    return schema;
  }
}

// 4. Validator Factory
namespace Validators {
  // Create validator from string
  export function createValidator(rule: string): ((value: any) => ValidationResult) | null {
    const parts = rule.split(':');
    const name = parts[0];
    const args = parts.slice(1);

    switch (name) {
      case 'required':
        return (value) => Validators.required(value);
      case 'type':
        return (value) => Validators.type(value, args[0]);
      case 'range':
        return (value) => Validators.range(value, Number(args[0]), Number(args[1]));
      case 'length':
        return (value) => Validators.length(value, Number(args[0]), Number(args[1]));
      case 'email':
        return (value) => Validators.email(value);
      case 'url':
        return (value) => Validators.url(value);
      case 'pattern':
        return (value) => Validators.pattern(value, new RegExp(args[0]));
      default:
        return null;
    }
  }
}

// 5. Function Argument Decorator
function validateArgs(...validators: Array<(value: any) => ValidationResult>) {
  return function (
    target: any,
    propertyKey: string,
    descriptor: PropertyDescriptor
  ) {
    const originalMethod = descriptor.value;

    descriptor.value = function (...args: any[]) {
      for (let i = 0; i < validators.length; i++) {
        if (validators[i]) {
          const result = validators[i](args[i]);

          if (!result.isValid) {
            throw new ValidationError(
              `Invalid argument ${i} for '${propertyKey}': ${result.errors.join(', ')}`,
              { argument: i, errors: result.errors }
            );
          }
        }
      }

      return originalMethod.apply(this, args);
    };

    return descriptor;
  };
}

// 6. Schema Validator Decorator
function validateSchema(schema: ValidationSchema) {
  return function (
    target: any,
    propertyKey: string,
    descriptor: PropertyDescriptor
  ) {
    const originalMethod = descriptor.value;

    descriptor.value = function (...args: any[]) {
      if (args.length > 0 && typeof args[0] === 'object') {
        const result = schema.validate(args[0]);

        if (!result.isValid) {
          throw new ValidationError(
            `Validation failed for '${propertyKey}': ${result.errors.join(', ')}`,
            { errors: result.errors }
          );
        }
      }

      return originalMethod.apply(this, args);
    };

    return descriptor;
  };
}

// 7. Object Validator
class ObjectValidator {
  // Validate nested object
  static validate(
    obj: any,
    schema: Record<string, Array<(value: any) => ValidationResult>>
  ): ValidationResult {
    const allErrors: string[] = [];

    for (const [field, validators] of Object.entries(schema)) {
      const value = obj[field];

      for (const validator of validators) {
        const result = validator(value);

        if (!result.isValid) {
          allErrors.push(...result.errors.map(err => `${field}: ${err}`));
        }
      }
    }

    return {
      isValid: allErrors.length === 0,
      errors: allErrors
    };
  }

  // Validate partial object
  static validatePartial(
    obj: any,
    schema: Record<string, Array<(value: any) => ValidationResult>>
  ): ValidationResult {
    const allErrors: string[] = [];

    for (const [field, validators] of Object.entries(schema)) {
      if (!(field in obj)) {
        continue;
      }

      const value = obj[field];

      for (const validator of validators) {
        const result = validator(value);

        if (!result.isValid) {
          allErrors.push(...result.errors.map(err => `${field}: ${err}`));
        }
      }
    }

    return {
      isValid: allErrors.length === 0,
      errors: allErrors
    };
  }
}

// 8. Async Validator
class AsyncValidator {
  // Validate async
  static async validate(
    value: any,
    validators: Array<(value: any) => Promise<ValidationResult>>
  ): Promise<ValidationResult> {
    const errors: string[] = [];

    for (const validator of validators) {
      const result = await validator(value);

      if (!result.isValid) {
        errors.push(...result.errors);
      }
    }

    return {
      isValid: errors.length === 0,
      errors
    };
  }

  // Check uniqueness async
  static async unique(
    value: any,
    checker: (value: any) => Promise<boolean>
  ): Promise<ValidationResult> {
    const isUnique = await checker(value);

    if (!isUnique) {
      return {
        isValid: false,
        errors: ['Value must be unique']
      };
    }

    return { isValid: true, errors: [] };
  }
}

// 9. Validation Middleware
class ValidationMiddleware {
  // Validate request data
  static validate(schema: ValidationSchema) {
    return async (data: any): Promise<void> => {
      const result = schema.validate(data);

      if (!result.isValid) {
        throw new ValidationError('Validation failed', {
          errors: result.errors
        });
      }
    };
  }
}

// Usage Examples
async function demonstrateParameterValidation() {
  console.log('=== Web TypeScript Parameter Validation Examples ===\n');

  // 1. Basic validators
  console.log('--- 1. Basic Validators ---');
  console.log('Required:', Validators.required('test'));
  console.log('Type:', Validators.type(123, 'number'));
  console.log('Range:', Validators.range(50, 0, 100));
  console.log('Email:', Validators.email('[email protected]'));

  // 2. Schema validation
  console.log('\n--- 2. Schema Validation ---');
  const userSchema = new ValidationSchema()
    .addRule('name', v => Validators.required(v))
    .addRule('name', v => Validators.length(v as string, 2, 50))
    .addRule('age', v => Validators.type(v, 'number'))
    .addRule('age', v => Validators.range(v as number, 0, 150))
    .addRule('email', v => Validators.email(v as string));

  const user1 = { name: 'John Doe', age: 30, email: '[email protected]' };
  const result1 = userSchema.validate(user1);
  console.log('Valid user:', result1);

  const user2 = { name: 'X', age: 200, email: 'invalid' };
  const result2 = userSchema.validate(user2);
  console.log('Invalid user:', result2);

  // 3. Object validation
  console.log('\n--- 3. Object Validation ---');
  const productSchema = {
    name: [v => Validators.required(v), v => Validators.length(v as string, 1, 100)],
    price: [v => Validators.type(v, 'number'), v => Validators.range(v as number, 0, Infinity)],
    url: [v => Validators.url(v as string)]
  };

  const product = { name: 'Widget', price: 29.99, url: 'https://example.com' };
  const productResult = ObjectValidator.validate(product, productSchema);
  console.log('Product validation:', productResult);

  // 4. Array validation
  console.log('\n--- 4. Array Validation ---');
  const arrayResult = Validators.array(
    [1, 2, 3, 4, 5],
    (item, index) => Validators.range(item as number, 0, 10)
  );
  console.log('Array validation:', arrayResult);

  console.log('\n=== All Parameter Validation Examples Completed ===');
}

// Custom Error import
class ValidationError extends Error {
  constructor(
    message: string,
    public code: string = 'VALIDATION_ERROR',
    public details?: any
  ) {
    super(message);
    this.name = 'ValidationError';
  }
}

// Export functions
export { Validators, ValidationSchema, ObjectValidator, AsyncValidator, ValidationMiddleware, validateArgs, validateSchema };
export { demonstrateParameterValidation };
export type { ValidationResult };