TypeScript
JavaScript
Best Practices
5 TypeScript Patterns I Swear By
December 5, 20246 min read
Discriminated unions, const assertions, template literal types and more — the TypeScript features that made me stop writing bugs.
After two years of full-time TypeScript I've settled on a handful of patterns that consistently eliminate entire categories of runtime bugs. Here are the five I reach for most often.
1. Discriminated unions over optional fields
ts
// ❌ optional fields — easy to forget to check
type Result = { data?: User; error?: string };
// ✅ discriminated union — TypeScript enforces the check
type Result =
| { ok: true; data: User }
| { ok: false; error: string };2. const assertions for literal inference
ts
const ROUTES = ["home", "about", "blog"] as const; type Route = (typeof ROUTES)[number]; // "home" | "about" | "blog"
3. Template literal types for string APIs
ts
type EventName<T extends string> = `on${Capitalize<T>}`;
type ButtonEvents = EventName<"click" | "focus">; // "onClick" | "onFocus"4. satisfies for validated literals
The satisfies operator validates a value against a type but keeps the narrowest inferred type — best of both worlds.
ts
type Config = Record<string, string | number>;
const config = {
port: 3000,
host: "localhost",
} satisfies Config;
// config.port is still inferred as 3000, not number5. Branded types for primitive safety
ts
type UserId = string & { readonly __brand: "UserId" };
type PostId = string & { readonly __brand: "PostId" };
function getPost(postId: PostId) { /* ... */ }
// getPost(userId) → compile error ✓