October 28, 20242 min read
TypeScript Patterns That Changed How I Code
TYPESCRIPTPATTERNSDX
After writing TypeScript for several years, certain patterns have become essential to how I build software. These aren't just type tricks—they fundamentally change how you think about code structure.
Discriminated Unions for State
Stop using boolean flags for state management:
// Instead of this
interface DataState {
loading: boolean;
error: Error | null;
data: User[] | null;
}
// Do this
type DataState =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'error'; error: Error }
| { status: 'success'; data: User[] };
Now TypeScript enforces that you handle every state correctly.
Builder Pattern with Type Safety
class QueryBuilder<T extends object = {}> {
private query: T = {} as T;
where<K extends string, V>(
key: K,
value: V
): QueryBuilder<T & Record<K, V>> {
return Object.assign(this, {
query: { ...this.query, [key]: value }
});
}
build(): T {
return this.query;
}
}
// Usage - fully typed!
const query = new QueryBuilder()
.where('name', 'John')
.where('age', 30)
.build();
// type: { name: string; age: number }
Branded Types for Domain Safety
Prevent mixing up primitive types that represent different things:
type Brand<T, B> = T & { __brand: B };
type UserId = Brand<string, 'UserId'>;
type OrderId = Brand<string, 'OrderId'>;
function getUser(id: UserId) { /* ... */ }
function getOrder(id: OrderId) { /* ... */ }
const userId = 'u_123' as UserId;
const orderId = 'o_456' as OrderId;
getUser(userId); // OK
getUser(orderId); // Type error!
The Result Type
Handle errors explicitly instead of throwing:
type Result<T, E = Error> =
| { ok: true; value: T }
| { ok: false; error: E };
async function fetchUser(id: string): Promise<Result<User>> {
try {
const user = await api.getUser(id);
return { ok: true, value: user };
} catch (e) {
return { ok: false, error: e as Error };
}
}
Conclusion
These patterns make impossible states unrepresentable. That's the real power of TypeScript—not just catching typos, but encoding business logic into the type system.