All Notes
Useful TypeScript Patterns I've Collected
Languages
typescriptpatterns
Discriminated Unions
One of the most powerful patterns in TypeScript. Use a literal field to distinguish between variants:
type ApiState<T> =
| { status: "idle" }
| { status: "loading" }
| { status: "success"; data: T }
| { status: "error"; error: Error };
function handleState(state: ApiState<User>) {
switch (state.status) {
case "idle":
return "Waiting...";
case "loading":
return "Loading...";
case "success":
return `Hello, ${state.data.name}`;
case "error":
return `Error: ${state.error.message}`;
}
}
The Builder Pattern with Types
Use generics and method chaining for type-safe builders:
class QueryBuilder<T extends Record<string, unknown>> {
private conditions: string[] = [];
where<K extends keyof T>(field: K, value: T[K]): this {
this.conditions.push(`${String(field)} = ${value}`);
return this;
}
build(): string {
return `SELECT * WHERE ${this.conditions.join(" AND ")}`;
}
}
Branded Types
Prevent mixing up values that have the same underlying type:
type UserId = string & { readonly __brand: "UserId" };
type PostId = string & { readonly __brand: "PostId" };
function getUser(id: UserId) { /* ... */ }
function getPost(id: PostId) { /* ... */ }
// getUser(postId); // Type error!