TypeScripttsconfig Reference

tsconfig.json Reference

The tsconfig.json file is the control center for the TypeScript compiler. It tells TypeScript which files to include, how to check them, what to emit, and how to resolve modules.

This reference covers the most important compilerOptions, organized by category, with explanations and recommended settings for modern projects.

File Structure

JSON
{
  "compilerOptions": {
    // Compiler behavior settings go here
  },
  "include": [
    "src/**/*"
  ],
  "exclude": [
    "node_modules",
    "dist"
  ],
  "files": [
    // Explicit list of files (rarely needed)
  ],
  "extends": "./tsconfig.base.json",
  "references": [
    // Project references for monorepos
    { "path": "../shared" }
  ]
}
Note
If you omit include, TypeScript includes all .ts, .tsx, and .d.ts files in the project root recursively (excluding node_modules).
Category Overview

Category

Key Options

Purpose

Type Checking

strict, noImplicitAny, strictNullChecks

How strict to be about type errors

Modules

module, moduleResolution, baseUrl, paths

How to resolve imports

Emit

target, outDir, declaration, sourceMap

What to output and where

Language & Env

lib, jsx, experimentalDecorators

Runtime environment and features

JavaScript Support

allowJs, checkJs

Working with .js files

Interop

esModuleInterop, allowSyntheticDefaultImports

CommonJS / ESM compatibility

Projects

composite, incremental, tsBuildInfoFile

Monorepo and build caching

Paths

baseUrl, paths, rootDir, outDir

Directory and alias configuration

Type Checking Options

These options control how strictly TypeScript checks your code. The strict flag is a shorthand that enables a bundle of checks at once.

JSON
{
  "compilerOptions": {
    "strict": true,
    // Shorthand that enables all of the following:
    // strictNullChecks, noImplicitAny, strictFunctionTypes,
    // strictBindCallApply, strictPropertyInitialization,
    // noImplicitThis, useUnknownInCatchVariables,
    // alwaysStrict (adds 'use strict' to emitted files)

    // Individual type-checking flags:
    "strictNullChecks": true,
    "noImplicitAny": true,
    "strictFunctionTypes": true,
    "strictBindCallApply": true,
    "strictPropertyInitialization": true,
    "noImplicitThis": true,
    "useUnknownInCatchVariables": true,
    "alwaysStrict": true,

    // Additional strict-style checks (not in 'strict'):
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "exactOptionalPropertyTypes": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "noUncheckedIndexedAccess": true,
    "noPropertyAccessFromIndexSignature": true
  }
}

Option

Default

What it does

strict

false

Enables all strict type-checking flags

strictNullChecks

false (true with strict)

null and undefined are distinct types

noImplicitAny

false (true with strict)

Error on variables inferred as any

strictFunctionTypes

false (true with strict)

Checks function parameter types contravariantly

strictPropertyInitialization

false (true with strict)

Class properties must be set in constructor

noImplicitThis

false (true with strict)

Error when this has implicit any type

useUnknownInCatchVariables

false (true with strict)

catch(e) type is unknown instead of any

noUnusedLocals

false

Error on declared but unused local variables

noUnusedParameters

false

Error on declared but unused function parameters

exactOptionalPropertyTypes

false

Disallows explicitly assigning undefined to optional props

noImplicitReturns

false

Error if not all code paths return a value

noFallthroughCasesInSwitch

false

Error on fallthrough between switch cases

noUncheckedIndexedAccess

false

Array/index access returns T | undefined

strictNullChecks in Detail

TS
// Without strictNullChecks:
let name: string = null;    // OK (null is assignable to anything)
let age: number = undefined; // OK

// With strictNullChecks:
let name: string = null;    // Error: null not assignable to string
let age: number = undefined; // Error: undefined not assignable to number

// Fix: use union types to allow null/undefined explicitly
let name: string | null = null;
let age: number | undefined = undefined;

// Optional chaining and nullish coalescing become essential
const upper = name?.toUpperCase() ?? 'anonymous';
noUncheckedIndexedAccess in Detail

TS
// Without noUncheckedIndexedAccess:
const arr: string[] = ['a', 'b', 'c'];
const first: string = arr[0]; // OK (but arr[10] would also type as string)

// With noUncheckedIndexedAccess:
const first: string | undefined = arr[0]; // index access adds | undefined
const safe = arr[0]?.toUpperCase(); // must handle undefined

// Also affects Record types
const dict: Record<string, number> = { a: 1 };
const val: number | undefined = dict['b']; // undefined is possible
Warning
Enabling noUncheckedIndexedAccess in an existing codebase will generate many new errors. Enable it on new projects or gradually fix violations in existing code.
Module Options

JSON
{
  "compilerOptions": {
    // What module format to emit
    "module": "NodeNext",      // Options: CommonJS, ESNext, NodeNext, Node16, Preserve

    // How to resolve modules from import paths
    "moduleResolution": "NodeNext", // Options: Node10, Node16, NodeNext, Bundler

    // Base directory for non-relative imports
    "baseUrl": "./src",

    // Path aliases
    "paths": {
      "@components/*": ["components/*"],
      "@utils/*": ["utils/*"],
      "@types/*": ["types/*"]
    },

    // Root directory of source files (for output mirroring)
    "rootDir": "./src",

    // Whether to allow importing .ts extension explicitly
    "allowImportingTsExtensions": true,

    // Resolve JSON files as modules
    "resolveJsonModule": true
  }
}

module value

Use case

CommonJS

Node.js projects using require/module.exports

ESNext

Bundler environments (webpack, Vite, esbuild)

NodeNext

Modern Node.js with native ESM support (.mjs/.cjs)

Node16

Same as NodeNext (alias)

Preserve

Pass through imports as-is (TS 5.4+, for bundlers)

None

Single-file scripts with no imports

moduleResolution value

When to use

Node10 (legacy)

Old projects — resolves like classic Node.js require

Node16

Modern Node.js with ESM — requires file extensions in imports

NodeNext

Same as Node16 but tracks future Node.js changes

Bundler

Vite, webpack, esbuild — no extension needed, supports package.json exports

Classic

Very old TypeScript — avoid

paths and baseUrl

JSON
{
  "compilerOptions": {
    "baseUrl": "./src",
    "paths": {
      // Single path alias
      "@config": ["config/index.ts"],

      // Wildcard alias (maps any @utils/* to src/utils/*)
      "@utils/*": ["utils/*"],

      // Multiple fallback paths
      "@shared/*": ["../shared/src/*", "shared/*"]
    }
  }
}

TS
// With the above config, you can write:
import { AppConfig } from '@config';
import { formatDate } from '@utils/date';
// Instead of:
import { AppConfig } from '../../config';
import { formatDate } from '../../../utils/date';
Note
When using paths in Node.js projects, you also need a runtime path resolver (e.g., tsconfig-paths or build tool support) because TypeScript only handles compile-time resolution.
Emit Options

JSON
{
  "compilerOptions": {
    // ECMAScript version to compile down to
    "target": "ES2020",   // Options: ES3, ES5, ES2015...ES2023, ESNext

    // Output directory for compiled files
    "outDir": "./dist",

    // Don't emit any output files (type-check only)
    "noEmit": true,

    // Generate .d.ts declaration files
    "declaration": true,

    // Output directory for declaration files (if different from outDir)
    "declarationDir": "./types",

    // Generate .d.ts.map files (source maps for declarations)
    "declarationMap": true,

    // Generate .js.map source maps
    "sourceMap": true,

    // Inline source map as data URL in the .js file
    "inlineSourceMap": false,

    // Remove all comments from emitted files
    "removeComments": false,

    // Don't emit on compilation errors
    "noEmitOnError": true,

    // Preserve const enums as their literal values
    "preserveConstEnums": false,

    // Emit helpers inline instead of importing from tslib
    "importHelpers": true,

    // Only emit declaration files (no JS)
    "emitDeclarationOnly": false
  }
}

target value

JS features available

Use case

ES3

Basic JS only

Legacy browser support

ES5

let, const, arrow functions

IE11 support

ES2015 (ES6)

Classes, Promises, generators

Modern browsers

ES2017

async/await natively

Node.js 8+, modern browsers

ES2020

Optional chaining, nullish coalescing, BigInt

Node.js 14+, modern browsers

ES2022

Top-level await, class fields

Node.js 16+

ESNext

Latest JS features

Always current with bundlers

Language and Environment Options

JSON
{
  "compilerOptions": {
    // Type definition libraries to include
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    // Common combinations:
    // Node.js: ["ES2020"] (no DOM) + install @types/node
    // Browser: ["ES2020", "DOM", "DOM.Iterable"]
    // Web Workers: ["ES2020", "WebWorker"]

    // JSX transformation mode
    "jsx": "react-jsx",
    // Options:
    // "preserve"     — output .jsx, no transformation
    // "react"        — React.createElement calls (React 16)
    // "react-jsx"    — import { jsx } from 'react/jsx-runtime' (React 17+)
    // "react-jsxdev" — same as react-jsx but development build
    // "react-native" — preserve JSX for React Native bundler

    // Enable legacy decorators (before Stage 3)
    "experimentalDecorators": false,

    // Enable metadata for decorators (used by reflection-based DI)
    "emitDecoratorMetadata": false,

    // Use define semantics for class fields
    "useDefineForClassFields": true
  }
}
lib in Detail

lib entry

What it provides

ES5

Basic ES5 types (Array, Object, Math, etc.)

ES2015 (ES6)

Promise, Map, Set, Symbol, generators, iterators

ES2017

SharedArrayBuffer, Atomics, async/await types

ES2020

BigInt, nullish coalescing, optional chaining, globalThis

ES2022

Array.at(), Object.hasOwn(), class static blocks

ESNext

Latest proposed features

DOM

Browser DOM APIs (document, window, HTMLElement, etc.)

DOM.Iterable

Iterable versions of DOM collections

WebWorker

Web Worker globals (self, postMessage, etc.)

ScriptHost

Windows Script Host environment

JavaScript Support Options

JSON
{
  "compilerOptions": {
    // Allow importing .js files
    "allowJs": true,

    // Type-check .js files (respects JSDoc annotations)
    "checkJs": true,

    // Max errors to report (0 = unlimited)
    "maxNodeModuleJsDepth": 0
  }
}

TS
// With allowJs and checkJs, TypeScript understands JSDoc:
// In a .js file:

/**
 * @param {string} name
 * @param {number} age
 * @returns {string}
 */
function greet(name, age) {
  return `${name} is ${age} years old`;
}

// TypeScript will check that callers pass (string, number) -> string
Tip
Use allowJs + checkJs when migrating a JavaScript codebase to TypeScript incrementally. Start by checking existing .js files without converting them.
Interop Options

JSON
{
  "compilerOptions": {
    // Enables interop between CommonJS and ES modules
    // Allows: import React from 'react' (default import for CJS module)
    "esModuleInterop": true,

    // Allows default imports from modules with no default export
    // (implied when esModuleInterop is true)
    "allowSyntheticDefaultImports": true,

    // Force consistent file name casing in imports
    "forceConsistentCasingInFileNames": true,

    // Isolate each file as its own module (required by Babel/esbuild/SWC)
    "isolatedModules": true
  }
}

TS
// Without esModuleInterop:
import * as React from 'react'; // required
import * as path from 'path';

// With esModuleInterop: true:
import React from 'react'; // default import works
import path from 'path';   // default import works
Note
isolatedModules: true is required when you use a transpiler like Babel, esbuild, or SWC that processes files individually (not the full TypeScript compiler). It disables features like const enums and ambient module declarations that need cross-file knowledge.
Project / Build Options

JSON
{
  "compilerOptions": {
    // Enable incremental compilation (stores build info)
    "incremental": true,

    // Where to store the incremental build info file
    "tsBuildInfoFile": "./.tsbuildinfo",

    // Enable project references (for monorepos and large projects)
    "composite": true,

    // Skip type checking of all declaration files (.d.ts)
    "skipLibCheck": true,

    // Skip type checking of default lib declaration files only
    "skipDefaultLibCheck": false
  }
}

Project references allow you to split large TypeScript projects into smaller pieces that can be compiled independently and incrementally.

JSON
// Root tsconfig.json in a monorepo
{
  "files": [],
  "references": [
    { "path": "./packages/shared" },
    { "path": "./packages/api" },
    { "path": "./packages/web" }
  ]
}

// packages/api/tsconfig.json
{
  "compilerOptions": {
    "composite": true,
    "outDir": "./dist",
    "rootDir": "./src"
  },
  "references": [
    { "path": "../shared" }  // depends on shared
  ]
}
Strict Mode Bundle

Setting "strict": true is equivalent to enabling all of these individually:

  • strictNullChecks — null and undefined are their own types

  • noImplicitAny — error when TypeScript infers any

  • strictFunctionTypes — function parameters checked contravariantly

  • strictBindCallApply — call/bind/apply are type-checked

  • strictPropertyInitialization — class properties must be initialized

  • noImplicitThis — error when this has type any

  • useUnknownInCatchVariables — catch(e) is unknown, not any

  • alwaysStrict — emits "use strict" in every file

Success
Always enable "strict": true on new projects. It catches a large class of runtime bugs at compile time and makes TypeScript genuinely safer to work with.
Recommended Configurations by Project Type

Node.js (Modern ESM)

JSON
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "lib": ["ES2022"],
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true,
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

React / Vite / Bundler

JSON
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "Bundler",
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "jsx": "react-jsx",
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true,
    "allowImportingTsExtensions": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    }
  },
  "include": ["src"],
  "references": [{ "path": "./tsconfig.node.json" }]
}

Next.js

JSON
{
  "compilerOptions": {
    "target": "ES2017",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "bundler",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "incremental": true,
    "plugins": [{ "name": "next" }],
    "paths": {
      "@/*": ["./src/*"]
    }
  },
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
  "exclude": ["node_modules"]
}
Common Mistakes

Mistake

Problem

Fix

Omitting strict

Many bugs TypeScript could catch are missed

Add "strict": true

Wrong moduleResolution for project type

Imports fail at runtime or compile time

Match moduleResolution to your runtime/bundler

baseUrl without paths entries

Short imports still fail

Add explicit paths entries or use bundler config

skipLibCheck: false with bad @types

Errors from third-party type files block builds

Use "skipLibCheck": true

target too high for runtime

Code fails in older runtimes

Match target to minimum supported runtime

No include/exclude

TS compiles test files into production output

Add "exclude": ["**/*.test.ts"]

esModuleInterop missing with CJS modules

import X from 'y' fails for CJS modules

Add "esModuleInterop": true