Learning Guides
Menu

Strict Mode and Compiler Options

3 min readEffective TypeScript

Strict Mode and Compiler Options

Strictness settings define how much TypeScript helps you. Strict mode is the best long‑term investment in correctness.

Item 115: Turn On strict

JSON
{
  "compilerOptions": {
    "strict": true
  }
}

This enables a set of safe defaults. If you can only turn on one flag, make it this.

Why it matters: strict mode catches entire classes of bugs early. The cost of fixing a bug is lowest at compile time, not in production.

Item 116: Know the Most Important Strict Flags

  • noImplicitAny: disallow implicit any
  • strictNullChecks: make null and undefined explicit
  • strictFunctionTypes: safer function variance
  • strictBindCallApply: safer call/apply/bind
  • strictPropertyInitialization: class fields must initialize
  • noImplicitThis: this must be typed

Why it matters: these flags target the most common sources of runtime errors in JavaScript—nulls, implicit any, and mis-typed callbacks.

Item 117: Enable noUncheckedIndexedAccess

This makes indexed access safer by adding undefined:

TYPESCRIPT
const arr = [1, 2, 3];
const first = arr[0]; // number | undefined

It forces you to handle missing values explicitly.

Why it matters: production crashes often come from unexpected undefined. This flag makes those cases visible in code review and CI.

Item 118: Use exactOptionalPropertyTypes

TYPESCRIPT
interface User {
  name: string;
  nickname?: string;
}
 
// With exactOptionalPropertyTypes: true
const user: User = { name: "Alice", nickname: undefined }; // Error

This clarifies the difference between “missing” and “explicitly undefined.”

Why it matters: optional properties are frequently used for partial data. This flag prevents subtle bugs when you accidentally set a property to undefined instead of omitting it.

Item 119: Keep useUnknownInCatchVariables

TYPESCRIPT
try {
  risky();
} catch (err) {
  // err is unknown
  if (err instanceof Error) {
    console.error(err.message);
  }
}

Why it matters: anything can be thrown in JavaScript. Treating caught errors as unknown forces you to handle non‑Error values safely.

Item 120: Use noImplicitOverride

TYPESCRIPT
class Base {
  save() {}
}
 
class Child extends Base {
  override save() {}
}

This prevents accidental method shadowing.

Why it matters: overrides are a common source of silent bugs—one typo can create a new method instead of overriding the base method.

Item 121: Add noUnusedLocals and noUnusedParameters

These flags keep code clean and signal dead logic early.

Why it matters: unused values often indicate unfinished refactors or logic that never runs—both are risk factors for bugs.

Item 122: Use noFallthroughCasesInSwitch

It prevents accidental switch fall‑through.

Why it matters: switch fall‑through is easy to miss in reviews and can cause subtle behavior changes when new cases are added.

Item 123: Avoid skipLibCheck Unless Necessary

It speeds builds but can hide real issues in dependencies.

Warning

If you turn on skipLibCheck, add a comment explaining why and track it as tech debt.

Why it matters: type errors in dependencies can leak into your code through unsafe types. Skipping checks can mask real integration issues.

JSON
{
  "compilerOptions": {
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "exactOptionalPropertyTypes": true,
    "useUnknownInCatchVariables": true,
    "noImplicitOverride": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true
  }
}

Key Takeaways

  1. Enable strict for all new projects.
  2. Add noUncheckedIndexedAccess and exactOptionalPropertyTypes for extra safety.
  3. Use useUnknownInCatchVariables and noImplicitOverride to avoid subtle bugs.
  4. Avoid skipLibCheck unless you have no alternative.

You're ready to write effective, safe, and scalable TypeScript!