Advanced TypeScript Patterns for Scale
TypeScript’s type system enables powerful patterns that go far beyond basic type annotations. These advanced patterns create self-documenting, maintainable code that scales across large development teams.
Branded Types for Domain Safety
// Prevent mixing different ID typestype UserId = string & { readonly __brand: unique symbol };type OrderId = string & { readonly __brand: unique symbol };
function createUserId(id: string): UserId { return id as UserId;}
// This prevents accidentally passing wrong ID typesfunction getOrder(userId: UserId, orderId: OrderId) { // Implementation}
Advanced Conditional Types
// Extract promise return typestype Awaited<T> = T extends Promise<infer U> ? U : T;
// Create type-safe API response handlerstype ApiResponse<T> = { success: true; data: T;} | { success: false; error: string;};
type ExtractData<T> = T extends ApiResponse<infer U> ? U : never;
Template Literal Types for API Routes
// Type-safe API route generationtype HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';type ApiRoute = `/api/${string}`;type ApiEndpoint<M extends HttpMethod, R extends ApiRoute> = `${M} ${R}`;
// Usagetype UserRoutes = | ApiEndpoint<'GET', '/api/users'> | ApiEndpoint<'POST', '/api/users'> | ApiEndpoint<'PUT', '/api/users/:id'>;
Architectural Patterns
Repository Pattern with Generics:
interface Repository<T, K = string> { findById(id: K): Promise<T | null>; save(entity: T): Promise<T>; delete(id: K): Promise<void>;}
class UserRepository implements Repository<User, UserId> { // Type-safe implementation}
Event-Driven Architecture:
type DomainEvent<T extends string, P = {}> = { type: T; payload: P; timestamp: Date;};
type UserEvents = | DomainEvent<'user.created', { userId: UserId }> | DomainEvent<'user.updated', { userId: UserId; changes: Partial<User> }>;
// Type-safe event handlerstype EventHandler<T extends DomainEvent<string, any>> = (event: T) => Promise<void>;
Performance Considerations
Lazy Type Loading:
// Avoid importing heavy types in hot pathstype LazyUser = () => Promise<import('./user-types').User>;
Const Assertions for Performance:
// More efficient than regular arrays/objectsconst statuses = ['pending', 'completed', 'failed'] as const;type Status = typeof statuses[number]; // Union type
Pro Tips
- Use
satisfies
operator for type checking without widening - Prefer
unknown
overany
for better type safety - Implement custom type guards for runtime validation
- Use module augmentation to extend third-party types safely
These patterns create codebases that are not just type-safe, but architecturally sound and maintainable at enterprise scale.