Real-World TypeScript Classes: Practical Examples
/ 9 min read
Real-World TypeScript Classes: Practical Examples
This guide provides real-world examples of TypeScript classes that you might encounter in production applications. Each example includes complete implementation details and best practices.
Table of Contents
- Real-World TypeScript Classes: Practical Examples
- Table of Contents
- Task Scheduler System
- HTTP Client with Interceptors
- Form Validator
- Image Processing Pipeline
Task Scheduler System
A complete task scheduling system with recurring tasks, priorities, and error handling:
type TaskPriority = 'low' | 'medium' | 'high';type TaskStatus = 'pending' | 'running' | 'completed' | 'failed';
interface TaskConfig { id: string; name: string; priority: TaskPriority; cronExpression?: string; timeout?: number; retryCount?: number; retryDelay?: number;}
class Task { private status: TaskStatus = 'pending'; private attempts: number = 0; private lastRun?: Date; private nextRun?: Date;
constructor( private config: TaskConfig, private handler: () => Promise<void> ) {}
async execute(): Promise<void> { this.status = 'running'; this.lastRun = new Date(); this.attempts++;
try { if (this.config.timeout) { await this.executeWithTimeout(); } else { await this.handler(); }
this.status = 'completed'; if (this.config.cronExpression) { this.scheduleNext(); } } catch (error) { await this.handleError(error); } }
private async executeWithTimeout(): Promise<void> { return Promise.race([ this.handler(), new Promise((_, reject) => setTimeout(() => reject(new Error('Task timeout')), this.config.timeout) ) ]); }
private async handleError(error: Error): Promise<void> { this.status = 'failed';
if (this.attempts < (this.config.retryCount || 0)) { await new Promise(resolve => setTimeout(resolve, this.config.retryDelay || 1000) ); await this.execute(); } }
private scheduleNext(): void { // Implementation of cron expression parsing and next run calculation this.nextRun = new Date(); // Simplified for example }
getStatus(): TaskStatus { return this.status; }
getNextRun(): Date | undefined { return this.nextRun; }}
class TaskScheduler { private tasks: Map<string, Task> = new Map(); private running: boolean = false;
addTask(config: TaskConfig, handler: () => Promise<void>): void { const task = new Task(config, handler); this.tasks.set(config.id, task); }
removeTask(id: string): boolean { return this.tasks.delete(id); }
async start(): Promise<void> { this.running = true; while (this.running) { await this.executeDueTasks(); await new Promise(resolve => setTimeout(resolve, 1000)); } }
stop(): void { this.running = false; }
private async executeDueTasks(): Promise<void> { const now = new Date(); const dueTasks = Array.from(this.tasks.values()) .filter(task => { const nextRun = task.getNextRun(); return nextRun && nextRun <= now; }) .sort((a, b) => this.comparePriority(a, b));
await Promise.all(dueTasks.map(task => task.execute())); }
private comparePriority(a: Task, b: Task): number { const priorityMap: Record<TaskPriority, number> = { high: 3, medium: 2, low: 1 }; return priorityMap[b.config.priority] - priorityMap[a.config.priority]; }}
// Usage exampleconst scheduler = new TaskScheduler();
scheduler.addTask( { id: 'backup-db', name: 'Database Backup', priority: 'high', cronExpression: '0 0 * * *', // Daily at midnight timeout: 30000, retryCount: 3, retryDelay: 5000 }, async () => { // Backup database implementation });
scheduler.start();HTTP Client with Interceptors
A complete HTTP client implementation with interceptors, retry logic, and response caching:
interface RequestConfig { url: string; method: 'GET' | 'POST' | 'PUT' | 'DELETE'; headers?: Record<string, string>; data?: unknown; timeout?: number; retries?: number; cache?: boolean; cacheTime?: number;}
interface ResponseData<T> { data: T; status: number; headers: Record<string, string>;}
type RequestInterceptor = (config: RequestConfig) => Promise<RequestConfig>;type ResponseInterceptor = <T>(response: ResponseData<T>) => Promise<ResponseData<T>>;type ErrorInterceptor = (error: Error) => Promise<never>;
class HttpClient { private requestInterceptors: RequestInterceptor[] = []; private responseInterceptors: ResponseInterceptor[] = []; private errorInterceptors: ErrorInterceptor[] = []; private cache: Map<string, { data: unknown; timestamp: number; cacheTime: number; }> = new Map();
addRequestInterceptor(interceptor: RequestInterceptor): void { this.requestInterceptors.push(interceptor); }
addResponseInterceptor(interceptor: ResponseInterceptor): void { this.responseInterceptors.push(interceptor); }
addErrorInterceptor(interceptor: ErrorInterceptor): void { this.errorInterceptors.push(interceptor); }
async request<T>(config: RequestConfig): Promise<ResponseData<T>> { try { // Apply request interceptors let currentConfig = { ...config }; for (const interceptor of this.requestInterceptors) { currentConfig = await interceptor(currentConfig); }
// Check cache if (currentConfig.cache) { const cachedResponse = this.getFromCache<T>(currentConfig.url); if (cachedResponse) return cachedResponse; }
// Make request let response = await this.makeRequest<T>(currentConfig);
// Apply response interceptors for (const interceptor of this.responseInterceptors) { response = await interceptor(response); }
// Cache response if (currentConfig.cache) { this.addToCache( currentConfig.url, response.data, currentConfig.cacheTime ); }
return response; } catch (error) { // Apply error interceptors let currentError = error; for (const interceptor of this.errorInterceptors) { try { await interceptor(currentError); } catch (newError) { currentError = newError; } } throw currentError; } }
private async makeRequest<T>( config: RequestConfig ): Promise<ResponseData<T>> { const controller = new AbortController(); const timeoutId = config.timeout ? setTimeout(() => controller.abort(), config.timeout) : null;
try { const response = await fetch(config.url, { method: config.method, headers: config.headers, body: config.data ? JSON.stringify(config.data) : undefined, signal: controller.signal });
const data = await response.json(); const headers: Record<string, string> = {}; response.headers.forEach((value, key) => { headers[key] = value; });
return { data, status: response.status, headers }; } finally { if (timeoutId) clearTimeout(timeoutId); } }
private getFromCache<T>(url: string): ResponseData<T> | null { const cached = this.cache.get(url); if (!cached) return null;
if (Date.now() - cached.timestamp > cached.cacheTime) { this.cache.delete(url); return null; }
return { data: cached.data as T, status: 200, headers: { 'x-cache': 'HIT' } }; }
private addToCache( url: string, data: unknown, cacheTime: number = 5 * 60 * 1000 ): void { this.cache.set(url, { data, timestamp: Date.now(), cacheTime }); }}
// Usage exampleconst http = new HttpClient();
// Add authentication interceptorhttp.addRequestInterceptor(async (config) => ({ ...config, headers: { ...config.headers, 'Authorization': `Bearer ${await getToken()}` }}));
// Add error handling interceptorhttp.addErrorInterceptor(async (error) => { if (error.message === 'Token expired') { await refreshToken(); throw new Error('Please retry request'); } throw error;});
// Make requestinterface User { id: string; name: string;}
const response = await http.request<User>({ url: 'https://api.example.com/users/1', method: 'GET', cache: true, cacheTime: 5 * 60 * 1000 // 5 minutes});Form Validator
A flexible form validation system:
type ValidationRule<T> = { validate: (value: T) => boolean; message: string;};
class FormValidator<T extends Record<string, any>> { private rules: Map<keyof T, ValidationRule<T[keyof T]>[]> = new Map(); private errors: Map<keyof T, string[]> = new Map();
addRule<K extends keyof T>( field: K, rule: ValidationRule<T[K]> ): void { const fieldRules = this.rules.get(field) || []; fieldRules.push(rule); this.rules.set(field, fieldRules); }
validate(data: T): boolean { this.errors.clear(); let isValid = true;
for (const [field, rules] of this.rules) { const fieldErrors: string[] = []; const value = data[field];
for (const rule of rules) { if (!rule.validate(value)) { fieldErrors.push(rule.message); isValid = false; } }
if (fieldErrors.length > 0) { this.errors.set(field, fieldErrors); } }
return isValid; }
getErrors(): Record<keyof T, string[]> { return Object.fromEntries(this.errors) as Record<keyof T, string[]>; }
getFieldErrors(field: keyof T): string[] { return this.errors.get(field) || []; }}
// Common validation rulesconst createRequiredRule = <T>(): ValidationRule<T> => ({ validate: (value: T) => value !== undefined && value !== null && value !== '', message: 'This field is required'});
const createMinLengthRule = (min: number): ValidationRule<string> => ({ validate: (value: string) => value.length >= min, message: `Minimum length is ${min} characters`});
const createEmailRule = (): ValidationRule<string> => ({ validate: (value: string) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value), message: 'Invalid email address'});
const createPasswordRule = (): ValidationRule<string> => ({ validate: (value: string) => /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{8,}$/.test(value), message: 'Password must contain at least 8 characters, one uppercase letter, one lowercase letter, and one number'});
// Usage exampleinterface RegistrationForm { username: string; email: string; password: string; confirmPassword: string;}
const validator = new FormValidator<RegistrationForm>();
validator.addRule('username', createRequiredRule());validator.addRule('username', createMinLengthRule(3));
validator.addRule('email', createRequiredRule());validator.addRule('email', createEmailRule());
validator.addRule('password', createRequiredRule());validator.addRule('password', createPasswordRule());
validator.addRule('confirmPassword', { validate: (value: string) => value === form.password, message: 'Passwords do not match'});
const form: RegistrationForm = { username: 'jo', email: 'invalid-email', password: 'weak', confirmPassword: 'different'};
if (!validator.validate(form)) { console.log(validator.getErrors());}Image Processing Pipeline
A class-based image processing pipeline:
interface ImageDimensions { width: number; height: number;}
interface ImageProcessingOptions { maxWidth?: number; maxHeight?: number; quality?: number; format?: 'jpeg' | 'png' | 'webp'; watermark?: { text: string; position: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'; };}
class ImageProcessor { private canvas: HTMLCanvasElement; private ctx: CanvasRenderingContext2D;
constructor() { this.canvas = document.createElement('canvas'); this.ctx = this.canvas.getContext('2d')!; }
async process( input: File | Blob | string, options: ImageProcessingOptions = {} ): Promise<Blob> { const image = await this.loadImage(input); const dimensions = this.calculateDimensions(image, options);
this.canvas.width = dimensions.width; this.canvas.height = dimensions.height;
// Clear canvas this.ctx.clearRect(0, 0, dimensions.width, dimensions.height);
// Draw image this.ctx.drawImage(image, 0, 0, dimensions.width, dimensions.height);
// Apply watermark if specified if (options.watermark) { this.applyWatermark(options.watermark); }
// Convert to desired format const format = options.format || 'jpeg'; const quality = options.quality || 0.9;
return new Promise<Blob>((resolve) => { this.canvas.toBlob( (blob) => resolve(blob!), `image/${format}`, quality ); }); }
private async loadImage(input: File | Blob | string): Promise<HTMLImageElement> { return new Promise((resolve, reject) => { const image = new Image(); image.onload = () => resolve(image); image.onerror = reject;
if (typeof input === 'string') { image.src = input; } else { image.src = URL.createObjectURL(input); } }); }
private calculateDimensions( image: HTMLImageElement, options: ImageProcessingOptions ): ImageDimensions { let { width, height } = image;
if (options.maxWidth && width > options.maxWidth) { height = (height * options.maxWidth) / width; width = options.maxWidth; }
if (options.maxHeight && height > options.maxHeight) { width = (width * options.maxHeight) / height; height = options.maxHeight; }
return { width, height }; }
private applyWatermark(watermark: NonNullable<ImageProcessingOptions['watermark']>): void { const { text, position } = watermark; const padding = 20;
this.ctx.font = '24px Arial'; this.ctx.fillStyle = 'rgba(255, 255, 255, 0.5)';
const metrics = this.ctx.measureText(text); const textWidth = metrics.width; const textHeight = 24;
let x: number; let y: number;
switch (position) { case 'top-left': x = padding; y = padding + textHeight; break; case 'top-right': x = this.canvas.width - textWidth - padding; y = padding + textHeight; break; case 'bottom-left': x = padding; y = this.canvas.height - padding; break; case 'bottom-right': x = this.canvas.width - textWidth - padding; y = this.canvas.height - padding; break; }
this.ctx.fillText(text, x, y); }}
// Usage exampleconst processor = new ImageProcessor();
const processImage = async (file: File) => { const processedImage = await processor.process(file, { maxWidth: 800, maxHeight: 600, quality: 0.8, format: 'webp', watermark: { text: ' My Company', position: 'bottom-right' } });
// Use the processed image const url = URL.createObjectURL(processedImage); const img = document.createElement('img'); img.src = url; document.body.appendChild(img);};These examples demonstrate real-world TypeScript class implementations with:
- Comprehensive error handling
- Type safety
- Proper abstraction
- Practical functionality
- Best practices for class design
Would you like me to:
- Add more specific examples?
- Add more implementation details to any example?
- Add more use cases or variations?