Essential patterns for writing maintainable, type-safe TypeScript code without drowning in excessive boilerplate.

The Article
The full story, in the order it happened.
TypeScript has become the default language for modern frontend engineering. However, writing types that aid productivity rather than getting in the way requires disciplined habits.
1. Avoid any — Embrace Unknown and Zod
Using any silently disables type-checking across your codebase. When handling untrusted external inputs (API payloads, query strings, local storage), use unknown alongside a schema validator like Zod:
import { z } from 'zod';
const UserSchema = z.object({
id: z.string(),
email: z.string().email(),
name: z.string(),
});
type User = z.infer<typeof UserSchema>;
2. Favor Discriminated Unions Over Optional Flags
Instead of creating large interfaces with dozens of optional properties, represent distinct states with discriminated unions:
type RequestState =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'success'; data: User[] }
| { status: 'error'; message: string };
This prevents invalid combinations of states (such as loading: true and error: "failed" existing simultaneously).

Tahmid Hasan
Web developer and UI designer building fast, accessible interfaces — and writing down what he learns along the way.
ABOUT ME