json TypeScript August 20, 2025

TypeScript Strict tsconfig Template

A maximally strict TypeScript configuration template that catches the most bugs at compile time — with explanations for every option.

typescripttsconfigstrictconfigtype-safety

Description

A production-ready tsconfig.json with every strict option enabled and documented. Each flag includes a brief explanation of what it catches.

The Config

{
  "$schema": "https://json.schemastore.org/tsconfig",
  "compilerOptions": {
    // ── Target & Module ──
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "lib": ["ES2022"],
    "esModuleInterop": true,

    // ── Output ──
    "outDir": "dist",
    "rootDir": "src",
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true,

    // ── Strict Mode (all flags) ──
    "strict": true,
    // Includes:
    //   "strictNullChecks": true        — null/undefined are distinct types
    //   "strictFunctionTypes": true     — contravariant function parameter checks
    //   "strictBindCallApply": true     — type-check bind/call/apply
    //   "strictPropertyInitialization": true — class props must be initialized
    //   "noImplicitAny": true           — no implicit 'any' types
    //   "noImplicitThis": true          — 'this' must have an explicit type
    //   "alwaysStrict": true            — emit "use strict" in every file
    //   "useUnknownInCatchVariables": true — catch variables are 'unknown', not 'any'

    // ── Additional Strictness ──
    "noUncheckedIndexedAccess": true,
    // Array/object index access returns T | undefined (catches out-of-bounds)

    "exactOptionalPropertyTypes": true,
    // Distinguishes between { x?: string } (missing) and { x: undefined }

    "noPropertyAccessFromIndexSignature": true,
    // Forces bracket notation for index signatures: obj["key"] not obj.key

    "noFallthroughCasesInSwitch": true,
    // Errors on switch case fall-through without break

    // ── Unused Code Detection ──
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    // Prefix unused params with _ to acknowledge: (_req, res) =>

    // ── Import Hygiene ──
    "verbatimModuleSyntax": true,
    // Forces explicit type imports: import type { Foo } from './foo'

    "resolveJsonModule": true,
    "isolatedModules": true,

    // ── Path Aliases ──
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"],
      "@domain/*": ["src/domain/*"],
      "@infra/*": ["src/infrastructure/*"]
    },

    // ── Build Performance ──
    "incremental": true,
    "tsBuildInfoFile": "./dist/.tsbuildinfo",
    "skipLibCheck": true
  },
  "include": ["src/**/*.ts"],
  "exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"]
}

What Each Strict Flag Catches

FlagCatches
strictNullChecksCannot read property of null/undefined
noImplicitAnyAccidentally untyped parameters
noUncheckedIndexedAccessArray out-of-bounds access
exactOptionalPropertyTypesundefined vs missing property confusion
noUnusedLocalsDead variables sitting in code
verbatimModuleSyntaxType imports erased at runtime causing issues

Usage

# Check types
npx tsc --noEmit

# Build
npx tsc

# Watch mode
npx tsc --watch