Back to Blog
TypeScriptJavaScriptBest PracticesEngineering

Why TypeScript Is Non-Negotiable for Serious JavaScript Projects

A pragmatic case for TypeScript, not from theoretical purity, but from real bugs caught, refactors enabled, and teams scaled.

December 5, 20243 min read

The Honest Argument

I'm not going to tell you TypeScript prevents all bugs. It doesn't. I'm going to tell you it prevents the most expensive class of bugs, the ones that surface in production at 2 AM because a property was renamed on the backend and nobody updated the three places it was used on the frontend.

The Bugs TypeScript Actually Prevents

1. Shape mismatches from API changes

// Before TypeScript: found in production
const price = product.price_usd; // backend renamed to `priceUsd` - silent undefined

// With TypeScript: caught at compile time
interface Product {
  priceUsd: number; // rename here breaks all callers immediately
}

2. Non-exhaustive switch cases

type Status = "active" | "inactive" | "suspended";

function getLabel(status: Status): string {
  switch (status) {
    case "active":
      return "Active";
    case "inactive":
      return "Inactive";
    // TypeScript errors: "suspended" is not handled
    // Without TS: returns undefined silently
  }
}

3. Null reference errors

// JavaScript: crashes if user is null
console.log(user.profile.avatar);

// TypeScript: forces you to handle the null case
const avatar = user?.profile?.avatar ?? "/default.png";

The Refactoring Multiplier

The biggest TypeScript benefit isn't bug prevention, it's making large refactors safe.

Rename a prop, change a function signature, restructure a data model. TypeScript tells you every call site that needs updating before you run a single test.

Without TypeScript, large refactors require:

  • Exhaustive grep searches
  • Manual QA across every affected screen
  • Fear that something was missed

With TypeScript, the compiler does the audit. You know when you're done.

Practical Setup for Next.js Projects

The tsconfig I use for every new project:

{
  "compilerOptions": {
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "exactOptionalPropertyTypes": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true
  }
}

strict: true is non-negotiable. The extra flags catch additional real-world issues:

  • noUncheckedIndexedAccess: arr[0] returns T | undefined, not T
  • exactOptionalPropertyTypes: differentiates absent vs. explicitly undefined

The "It Slows Down Development" Myth

TypeScript does slow you down, for the first two hours of a project. After that, it speeds you up, because:

  • Autocomplete is accurate (no more guessing object shapes)
  • The compiler catches errors before you Alt+Tab to your browser
  • Documentation is embedded in types (no need to Google function signatures)

The teams I've seen reject TypeScript are usually the same teams spending two hours per sprint tracking down Cannot read properties of undefined errors.

Start Strict, Stay Strict

The worst TypeScript is strict: false with // @ts-ignore peppered everywhere. That's the worst of both worlds: no safety, added friction.

The best TypeScript is strict typing from day one, colocated types, and a culture where any is a code review trigger, not a shortcut.

If you're on a new project today: start with strict: true. You'll thank yourself at the six-month mark.