Enabling TypeScript's strict mode is the single biggest quality improvement you can make to a JavaScript codebase. This guide covers patterns every TS developer should know in 2026.
🔒 Enable Strict Mode
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true,
"exactOptionalPropertyTypes": true
}
}
🎯 Pattern 1: Discriminated Unions
type Result<T> =
| { ok: true; data: T }
| { ok: false; error: string };
function handle(r: Result<User>) {
if (r.ok) return r.data.name; // narrowed to User
return r.error; // narrowed to string
}
🏷️ Pattern 2: Branded Types
type UserId = string & { __brand: 'UserId' };
type Email = string & { __brand: 'Email' };
function sendEmail(to: Email) { /* ... */ }
sendEmail("foo@bar.com" as Email); // OK
sendEmail("not-validated"); // ❌ Type error
🚫 Banish any
Use unknown for truly unknown values, then narrow with type guards. Configure ESLint with @typescript-eslint/no-explicit-any to enforce this team-wide.
💡 Pro Tip: Use
satisfies instead of type assertions to keep narrow inferred types while validating shape.