TypeScript Configuration and tsconfig

The tsconfig.json file controls how TypeScript compiles the project. It defines which files to include, which to exclude, where to output the compiled JavaScript, and which language features and strictness rules to enforce. Every production TypeScript project uses a tsconfig.json to ensure consistent and predictable compilation behavior across all developers and environments.

Creating tsconfig.json

// Run in the project root
tsc --init

This generates a tsconfig.json with all common options listed as comments. The file uses JSON format with one root object containing a compilerOptions key and optional include, exclude, and files keys.

tsconfig.json Structure

  tsconfig.json
  +---------------------------------------------+
  |                                             |
  |  {                                          |
  |    "compilerOptions": { ... },   ← How to compile
  |    "include": [ ... ],           ← What to include
  |    "exclude": [ ... ],           ← What to exclude
  |    "files": [ ... ]              ← Explicit file list
  |  }                               |
  |                                             |
  +---------------------------------------------+

Minimal Working tsconfig.json

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "CommonJS",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}

compilerOptions — Core Settings

target

Specifies the JavaScript version the TypeScript compiler outputs. Modern browsers support ES2020 and later. Node.js supports ES2022+.

"target": "ES5"        // Wide browser support (older browsers)
"target": "ES2015"     // Arrow functions, classes, let/const
"target": "ES2020"     // Optional chaining, nullish coalescing (recommended)
"target": "ES2022"     // Latest stable features
"target": "ESNext"     // Latest TypeScript-supported features

module

Defines the module system used in the compiled JavaScript output.

"module": "CommonJS"   // Node.js projects (require/exports)
"module": "ESNext"     // Modern browser/bundler projects (import/export)
"module": "NodeNext"   // Modern Node.js with native ESM

outDir and rootDir

"outDir": "./dist"   // All compiled .js files go here
"rootDir": "./src"   // TypeScript source files start here

  Project Structure:
  ├── src/
  │   ├── index.ts       ← TypeScript source
  │   └── utils.ts       ← TypeScript source
  ├── dist/
  │   ├── index.js       ← Compiled output
  │   └── utils.js       ← Compiled output
  └── tsconfig.json

strict

The strict flag enables a group of strict type-checking rules simultaneously. Setting "strict": true is strongly recommended for all new projects — it catches the most bugs and enforces the best practices.

"strict": true   // Enables ALL of the following at once:

// What "strict" turns on:
"noImplicitAny": true,           // Disallow implied 'any' type
"strictNullChecks": true,        // null/undefined must be handled
"strictFunctionTypes": true,     // Stricter function type checking
"strictBindCallApply": true,     // Strict bind/call/apply checks
"strictPropertyInitialization": true, // Class properties must be initialized
"noImplicitThis": true,          // Disallow implicit 'this' as 'any'
"useUnknownInCatchVariables": true    // catch errors typed as 'unknown'

Individual Strict Options

OptionWhat It EnforcesExample Error Caught
noImplicitAnyAll variables must have explicit or inferred typesfunction greet(name) — name has implicit any
strictNullChecksnull/undefined not assignable to other typeslet name: string = null — error
noUnusedLocalsReport unused local variablesDeclaring a variable and never using it
noUnusedParametersReport unused function parametersA function parameter that is never used
noImplicitReturnsAll code paths must return a valueFunction missing a return in one branch
exactOptionalPropertyTypesOptional props cannot be set to undefined explicitlySetting obj.optProp = undefined

Path and Module Options

{
  "compilerOptions": {
    "baseUrl": "./src",           // Base for non-relative imports
    "paths": {
      "@utils/*": ["utils/*"],    // Alias: @utils/math → src/utils/math
      "@models/*": ["models/*"]
    },
    "moduleResolution": "Node",   // How TypeScript finds modules
    "esModuleInterop": true,      // Better CommonJS/ESM compatibility
    "resolveJsonModule": true,    // Allow importing .json files
    "allowSyntheticDefaultImports": true  // Allow default imports
  }
}

Source Maps and Debugging

{
  "compilerOptions": {
    "sourceMap": true,         // Generate .js.map files for debugging
    "inlineSourceMap": false,  // Embed source map inside .js file
    "declaration": true,       // Generate .d.ts declaration files
    "declarationDir": "./types" // Where to put declaration files
  }
}

Source maps connect the compiled JavaScript lines back to the original TypeScript lines. When an error appears in the browser or Node.js, the stack trace points to the TypeScript file, not the compiled JavaScript.

Include, Exclude, and Files

{
  "include": [
    "src/**/*"           // All files in src folder recursively
  ],
  "exclude": [
    "node_modules",      // Never compile node_modules
    "dist",              // Skip output folder
    "**/*.test.ts",      // Skip test files
    "**/*.spec.ts"
  ],
  "files": [
    "src/index.ts",      // Explicitly include specific files
    "src/globals.d.ts"
  ]
}

Project References — Monorepo Setup

Large projects with multiple sub-packages use project references to compile each package independently and share types between them.

  Monorepo Structure:
  ├── packages/
  │   ├── shared/         tsconfig.json (base types)
  │   ├── frontend/       tsconfig.json (references shared)
  │   └── backend/        tsconfig.json (references shared)
  └── tsconfig.json       (root — references all packages)

  // packages/frontend/tsconfig.json
  {
    "compilerOptions": {
      "outDir": "./dist",
      "composite": true   // Required for project references
    },
    "references": [
      { "path": "../shared" }
    ]
  }

Common tsconfig Presets

Project TypeKey Settings
Node.js Backendtarget: "ES2022", module: "NodeNext", moduleResolution: "NodeNext"
Browser App (bundled)target: "ES2020", module: "ESNext", moduleResolution: "Bundler"
React Appjsx: "react-jsx", strict: true, esModuleInterop: true
Library / Packagedeclaration: true, declarationDir: "./types", composite: true

Complete Production-Ready tsconfig.json

{
  "compilerOptions": {
    // Output settings
    "target": "ES2020",
    "module": "CommonJS",
    "lib": ["ES2020", "DOM"],
    "outDir": "./dist",
    "rootDir": "./src",

    // Module resolution
    "moduleResolution": "Node",
    "esModuleInterop": true,
    "resolveJsonModule": true,
    "allowSyntheticDefaultImports": true,

    // Strict type checking
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true,

    // Debugging
    "sourceMap": true,
    "declaration": true,
    "declarationDir": "./types",

    // Path aliases
    "baseUrl": "./src",
    "paths": {
      "@utils/*": ["utils/*"],
      "@models/*": ["models/*"]
    }
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist", "**/*.test.ts"]
}

Extending a Base Config

A project can extend another tsconfig.json using the extends key. This is useful for sharing common settings across multiple packages in a monorepo.

// tsconfig.base.json — shared settings
{
  "compilerOptions": {
    "strict": true,
    "esModuleInterop": true,
    "target": "ES2020"
  }
}

// packages/frontend/tsconfig.json — extends base
{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "outDir": "./dist",
    "rootDir": "./src",
    "jsx": "react-jsx"
  }
}

tsconfig Key Options Summary

OptionPurposeRecommended Value
targetJS version to outputES2020
strictEnable all strict checkstrue
outDirCompiled output directory./dist
rootDirTypeScript source root./src
sourceMapEnable debugging mapstrue
declarationGenerate .d.ts filestrue for libraries
esModuleInteropBetter module compatibilitytrue
resolveJsonModuleImport JSON filestrue
noUnusedLocalsWarn on unused variablestrue
noImplicitReturnsAll paths must returntrue

Leave a Comment