~/TechPurAI
~/blog/typescript-strict-mode-two-year-old-project
TypeScript

I turned on TypeScript's strict mode in a two-year-old project. Here's what broke.

The project started in 2024 with "strict": false, because deadlines, and it stayed that way through two years of feature work. Flipping it on was supposed to be a quick Friday-afternoon cleanup. It took most of the week — and turned up bugs that had been sitting in production the entire time.

What strict mode actually turns on

"strict": true isn't one setting, it's shorthand for seven:

json
{
  "strict": true
  // equivalent to:
  // noImplicitAny, strictNullChecks, strictFunctionTypes,
  // strictBindCallApply, strictPropertyInitialization,
  // noImplicitThis, alwaysStrict
}

Two of those seven accounted for almost every error I saw: strictNullChecks and noImplicitAny. The other five barely registered.

strictNullChecks found three real bugs

Without it, null and undefined are assignable to everything — a string parameter happily accepts null and TypeScript says nothing. With it on, this stopped compiling:

typescript
function getDisplayName(user: User): string {
  return user.nickname.toUpperCase();
}

user.nickname was typed string | undefined in the database layer, and three call sites were passing users who'd never set one. In production this had been throwing Cannot read properties of undefined for months, caught by a generic error boundary that just showed "Something went wrong" — so it never showed up as a crash report anyone investigated. The type error pointed straight at the fix:

typescript
function getDisplayName(user: User): string {
  return (user.nickname ?? user.email).toUpperCase();
}

Two more of the same shape turned up elsewhere. Three real, silent bugs, found without writing a single test.

noImplicitAny mostly found sloppy code, not bugs

This one was noisier and lower-value. Old utility functions like this compiled fine before:

typescript
function groupBy(items, keyFn) {
  // items and keyFn are both `any`
}

noImplicitAny flagged every one. Fixing them was mechanical — add the parameter types — and didn't uncover any bugs, just years of accumulated untyped glue code. Necessary, but it was hours of typing signatures, not hours of finding problems.

Where the value actually was

strictNullChecks found real, currently-live bugs. noImplicitAny mostly found technical debt. If a large codebase can't take the whole strict flag at once, strictNullChecks on its own is where the return on investment is — turn it on first, separately, and see what it finds before committing to the rest.

The genuine false positive

One category of error was correct according to the type system and wrong according to reality:

typescript
const cache = new Map<string, User>();

function getUser(id: string): User {
  if (!cache.has(id)) {
    cache.set(id, fetchUser(id));
  }
  return cache.get(id); // Error: User | undefined is not assignable to User
}

TypeScript can't know that the .set() call two lines above guarantees .get() will find something — it checks Map.get()'s return type in isolation, not the surrounding control flow. The fix is either a non-null assertion (cache.get(id)!) with a comment explaining why it's safe, or restructuring to avoid the two-step lookup:

typescript
function getUser(id: string): User {
  const existing = cache.get(id);
  if (existing) return existing;

  const user = fetchUser(id);
  cache.set(id, user);
  return user;
}

The second version is more code but doesn't need an escape hatch — worth it for anything that isn't a one-off.

Doing it incrementally

Turning on strict for the whole project at once produced 47 errors across 30 files, which is exactly the wall of errors that makes teams give up and revert. tsconfig.json supports enabling the flags one at a time:

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

Ship that alone, let it sit for a sprint, then add the next flag. Each one lands as a reviewable, bisectable change instead of one 47-error diff nobody reads carefully.

Was it worth a week

Three production bugs found before a user reported them, against a week of mostly mechanical fixes — yes, but only because strictNullChecks did the heavy lifting. If your project doesn't have two years of untyped null handling built up, don't expect the same ratio. The value is proportional to how long the codebase went without it.

VK

Vijay Kumar

Founder of TechPurAI — writing hands-on tutorials and honest tool breakdowns.

LinkedIn ↗