TypeScript Design Patterns
Apply design patterns in TypeScript — leveraging the type system for safer implementations of Builder, Strategy, Observer, and Repository patterns.
55 min•By Priygop Team•Last updated: Feb 2026
Patterns with TypeScript
- Builder Pattern: class QueryBuilder<T> with chained methods returning refined types — .where() returns QueryBuilder<T & { filtered: true }>. Compile-time validation of builder usage
- Strategy Pattern: interface SortStrategy<T> { sort(items: T[]): T[] } — swap algorithms at runtime. Generic type parameter ensures strategy matches data type
- Repository Pattern: interface Repository<T extends Entity> { find(id: string): Promise<T | null>; save(entity: T): Promise<T> } — abstract data access. Swap implementations (SQL, MongoDB, in-memory for tests)
- Observer/Event Emitter: class TypedEventEmitter<Events extends Record<string, any[]>> — type-safe event names and listener arguments. emit<K extends keyof Events>(event: K, ...args: Events[K])
- Dependency Injection: class Container with typed bindings — container.bind<ILogger>(TOKENS.Logger).to(ConsoleLogger). Inversify or tsyringe for production DI containers
- Result Type: type Result<T, E> = { ok: true; value: T } | { ok: false; error: E } — explicit error handling without exceptions. Forces callers to handle both success and failure