Advanced TypeScript Patterns for Enterprise Applications
TypeScript has evolved from a nice-to-have to an essential tool for building maintainable enterprise-scale applications. However, many teams only scratch the surface of its capabilities. Let's explore advanced patterns that separate senior engineers from the rest.
Utility Types and Conditional Types
Leverage TypeScript's built-in utility types and create your own conditional types for type-safe abstractions:
// Advanced utility type composition
type DeepPartial = T extends object ? {
[P in keyof T]?: DeepPartial;
} : T;
type ApiResponse = {
data: T;
meta: {
page: number;
total: number;
};
errors?: Array<{ code: string; message: string }>;
};
// Conditional type based on environment
type Config = T extends 'development'
? { debug: true; verboseLogging: boolean }
: { debug: false; cacheTTL: number };
Branded Types for Runtime Safety
Create nominal typing in TypeScript's structural type system to prevent primitive obsession bugs:
// Branded types for domain primitives
type UserId = string & { readonly brand: unique symbol };
type Email = string & { readonly brand: unique symbol };
const createUserId = (id: string): UserId => {
if (!isValidUserId(id)) throw new Error('Invalid user ID');
return id as UserId;
};
const createEmail = (email: string): Email => {
if (!isValidEmail(email)) throw new Error('Invalid email');
return email as Email;
};
// Now these are compile-time errors:
const userId: UserId = 'user123'; // Error!
const email: Email = 'test@example.com'; // Error!
Advanced Generic Constraints
Use complex generic constraints to create flexible yet type-safe APIs:
// Constrained generics with multiple constraints
interface Entity {
id: string;
createdAt: Date;
}
interface Repository {
getById(id: string): Promise;
findBy(field: K, value: T[K]): Promise;
update(entity: Partial & Pick): Promise;
}
// Factory function with inference
function createRepository(): Repository {
return {
// Implementation with full type safety
};
}
Template Literal Types for API Routes
Use template literal types to create type-safe API routing systems:
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
type Resource = 'users' | 'posts' | 'comments';
type ApiRoute = `/${Resource}/${string}` | `/${Resource}`;
type RouteConfig = {
[K in ApiRoute]: {
methods: HttpMethod[];
validation?: Record;
};
};
// Fully type-safe route configuration
const routes: RouteConfig = {
'/users': { methods: ['GET', 'POST'] },
'/users/:id': { methods: ['GET', 'PUT', 'DELETE'] },
// '/products' would be a compile-time error
};
Mastering these advanced TypeScript patterns will elevate your code quality, catch bugs at compile time, and make your codebase significantly more maintainable as it scales to millions of lines of code.