Back to Blog
6 min read

TypeScript Configuration: A Journey Through Project Maturity

TypeScriptConfigurationBest PracticesProject Management

TypeScript Configuration: A Journey Through Project Maturity

Every TypeScript project starts with good intentions and a `tsconfig.json` file. But as projects mature, so too must their TypeScript configuration. Here's the journey I've observed across multiple projects.

Stage 1: The Permissive Beginning

Most projects start with a relaxed configuration:

{
  "compilerOptions": {
    "target": "es2017",
    "module": "commonjs",
    "strict": false,
    "noImplicitAny": false
  }
}

This works great for getting started quickly, especially when migrating from JavaScript. But it doesn't provide the full benefits of TypeScript's type system.

Stage 2: Tightening the Screws

As the codebase grows and bugs start appearing, teams typically enable more strict checking:

{
  "compilerOptions": {
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true
  }
}

Stage 3: Production-Ready Configuration

Mature projects often end up with comprehensive configurations:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "esnext",
    "moduleResolution": "bundler",
    "strict": true,
    "exactOptionalPropertyTypes": true,
    "noUncheckedIndexedAccess": true,
    "noImplicitOverride": true
  }
}

The Path Dependency Problem

The challenge is that it's much easier to start strict than to migrate to strict later. Once you have thousands of lines of loosely-typed code, the migration becomes daunting.

My Recommended Starting Point

For new projects, I recommend starting with a moderately strict configuration:

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "esnext",
    "moduleResolution": "bundler",
    "strict": true,
    "noUnusedLocals": true,
    "noImplicitReturns": true,
    "allowUnusedLabels": false,
    "allowUnreachableCode": false
  }
}

Incremental Migration Strategy

For existing projects, enable strict checking incrementally:

  • Start with `noImplicitAny: true`
  • Add `strictNullChecks: true`
  • Enable other strict flags one by one
  • Use `// @ts-expect-error` comments for temporary exceptions

The key is consistency and gradual improvement. TypeScript's configuration should evolve with your project's maturity, not fight against it.