TypeScript has revolutionized JavaScript development by providing strong typing and advanced object-oriented features. In this post, we'll explore sophisticated design patterns that can help you build more maintainable and robust TypeScript applications.

The Power of Discriminated Unions

Discriminated unions (also known as tagged unions) are one of TypeScript's most powerful features for modeling complex state:

type NetworkState = | { state: "loading" } | { state: "success", data: ResponseData } | { state: "error", error: Error };

This pattern allows the compiler to narrow types based on the discriminant property ("state" in this example), providing excellent type safety.

Builder Pattern with Method Chaining

The Builder pattern is particularly elegant in TypeScript, allowing for fluent interfaces with full type safety:

class QueryBuilder { private query: Query = {}; where(condition: Condition): this { this.query.where = condition; return this; } orderBy(field: string, direction: "asc" | "desc"): this { this.query.orderBy = { field, direction }; return this; } build(): Query { return { ...this.query }; } }

Factory Pattern with Generic Types

TypeScript's generics make the Factory pattern even more powerful, allowing for type-safe creation of objects:

interface Repository<T> { find(id: string): Promise<T>; save(entity: T): Promise<void>; } function createRepository<T>(entityType: string): Repository<T> { // Implementation details return { find: async (id) => { /* ... */ } as T, save: async (entity) => { /* ... */ } }; }