Web Development
Advanced TypeScript Patterns: 2025 Edition
Master discriminated unions, branded types, type-safe builders, and functional utilities for robust frontend and backend apps.
TechDevDex Team
1/16/2025
18 min
#TypeScript#Patterns#Types#Generics#Functional
Discriminated unions
Create safe state machines with a kind field to enable exhaustive checks.
type LoadState =
| { kind: 'idle' }
| { kind: 'loading' }
| { kind: 'success'; data: string }
| { kind: 'error'; message: string };
function render(state: LoadState) {
switch (state.kind) {
case 'idle': return 'Idle';
case 'loading': return 'Loading…';
case 'success': return state.data;
case 'error': return state.message;
}
}
Branded types
Prevent accidental mixing of primitives.
type Brand<T, B> = T & { __brand: B };
type UserId = Brand<string, 'UserId'>;
function getUser(id: UserId) {/* … */}
Type-safe builder
type WithTitle = { title: string };
type WithBody = { body: string };
type Article = WithTitle & WithBody;
const builder = () => {
let a = {} as Partial<Article>;
return {
title(t: string) { a.title = t; return this; },
body(b: string) { a.body = b; return this; },
build(): Article { return { title: a.title!, body: a.body! }; }
};
};
Functional utilities
Prefer small, composable helpers with precise types.
export function mapValues<T extends object, R>(
obj: T,
fn: <K extends keyof T>(v: T[K], k: K) => R
): { [K in keyof T]: R } {
const out = {} as { [K in keyof T]: R };
(Object.keys(obj) as Array<keyof T>).forEach(k => {
out[k] = fn(obj[k], k);
});
return out;
}
Conclusion
Use strong types to express invariants. Lean on unions, brands, and small utilities to keep code safe and maintainable.