The TypeScript Strict Mode Survival Guide

Marcus Webb · · 19 views

Enabling strict mode in an existing TypeScript project can feel overwhelming. Here is a systematic approach to tackling each error category without losing your mind.

Why Strict Mode?

TypeScript's strict flag enables a bundle of checks: strictNullChecks, strictFunctionTypes, strictBindCallApply, noImplicitAny, and more. Together they eliminate entire categories of runtime errors at compile time.

Step 1: Enable One Flag at a Time

Rather than flipping "strict": true and drowning in errors, enable flags individually:

{
  "compilerOptions": {
    "strictNullChecks": true
  }
}

Fix those errors, commit, then add the next flag.

Step 2: Tackle noImplicitAny First

noImplicitAny produces the most errors in a typical codebase but is also the most mechanical to fix. Use your editor's bulk-apply suggestions or a codemod like ts-migrate.

Step 3: Handle null and undefined

Once strictNullChecks is on, you will see errors wherever you assume a value is present. Common patterns:

// Before
function greet(name: string | undefined) {
  return `Hello, ${name.toUpperCase()}`; // error!
}

// After: narrowing
function greet(name: string | undefined) {
  if (!name) return "Hello, stranger";
  return `Hello, ${name.toUpperCase()}`;
}

// After: non-null assertion (use sparingly)
function greet(name: string | undefined) {
  return `Hello, ${name!.toUpperCase()}`;
}

Step 4: Use satisfies for Safer Inference

The satisfies operator (TypeScript 4.9+) lets you validate a value against a type while keeping a narrower inferred type:

const config = {
  port: 8080,
  host: "localhost"
} satisfies Partial<ServerConfig>;

config.port; // inferred as number, not number | undefined

Conclusion

Strict mode is an investment. The upfront cost pays dividends in refactoring confidence and reduced production incidents.