TypeScript tsconfig.json File

The tsconfig.json file is the heart of any TypeScript project. It acts as the "instruction manual" for the TypeScript compiler (tsc), determining exactly how your source code should be transformed into JavaScript. Without this file, the compiler would have no idea which files to look at or which version of JavaScript your environment supports.

Developer Tip: You can quickly generate a fresh tsconfig.json file with all the default options commented out by running tsc --init in your project's terminal.

 

What is tsconfig.json?

The tsconfig.json file is a JSON configuration file located at the root of your project directory. Its presence signals that the directory is the root of a TypeScript project. It allows developers to manage complex build processes by specifying how strict the type-checking should be, where the compiled output should go, and which files should be ignored (like test files or legacy code).

Best Practice: Always include a tsconfig.json file in your project, even for small scripts. It ensures consistency across different developers' machines and CI/CD environments.

 

Key Sections of tsconfig.json

compilerOptions
This is the largest section. It holds the configurations that dictate how the compiler behaves, such as language features and output directory settings.

include
This uses "glob" patterns (like * and **) to tell TypeScript which folders and files should be considered part of the project.

exclude
This tells the compiler which files to ignore. Even if a file is matched by the include list, it will be skipped if it appears here.

files
Unlike include, this is used for a specific, hard-coded list of files. It’s rarely used in modern projects because include is much more flexible.

Common Mistake: Forgetting that exclude only filters files found via the include setting. If you explicitly import a file in your code, TypeScript will still compile it even if it's in the exclude list.

Example of a tsconfig.json File

{
  "compilerOptions": {
    "target": "ES6",                           // Compile to modern ES6 JavaScript
    "module": "commonjs",                      // Use Node.js style module resolution
    "strict": true,                            // Enable maximum type safety
    "esModuleInterop": true,                   // Simplifies importing non-ESM libraries
    "skipLibCheck": true,                      // Saves time by not checking third-party types
    "forceConsistentCasingInFileNames": true,  // Prevents bugs on different Operating Systems
    "outDir": "./dist"                         // Where the compiled JS files will live
  },
  "include": [
    "src/**/*.ts"                              // Look for all .ts files inside the src folder
  ],
  "exclude": [
    "node_modules",                            // Never compile external dependencies
    "**/*.test.ts"                             // Ignore test files during build
  ]
}

 

Common Compiler Options

target
Specifies the version of JavaScript the output should be. If you are targeting older browsers, you might choose "ES5". For modern Node.js environments, "ES2020" or "ESNext" is common.
Example: "target": "ES6".

module
This defines the module syntax of the output. "commonjs" is standard for Node.js, while "esnext" is used for modern web bundlers like Vite or Webpack.
Example: "module": "commonjs".

strict
The most important setting for code quality. Setting this to true enables a suite of checks that prevent common bugs, such as null pointer exceptions and implicit any types.
Example: "strict": true.

Best Practice: Always start new projects with "strict": true. It is much harder to enable strict mode later once a project has grown large and contains "loose" code.

esModuleInterop
In older days, importing CommonJS modules into ES modules was messy. Setting this to true allows you to use import React from 'react' instead of import * as React from 'react'.
Example: "esModuleInterop": true.

skipLibCheck
By default, TypeScript checks the types of every library in your node_modules. This can be very slow. Setting this to true skips those checks, assuming the library authors already handled it.
Example: "skipLibCheck": true.

forceConsistentCasingInFileNames
Windows is case-insensitive, but Linux is case-sensitive. This setting ensures that if you import MyFile.ts, you don't accidentally write import './myfile', which would break your build on a Linux server.
Example: "forceConsistentCasingInFileNames": true.

 

Include and Exclude Fields

include
You should almost always point this to your source directory. Using "src/**/*" ensures that any TypeScript file you create in src is automatically tracked.

exclude
The most common use for this is ignoring node_modules. You might also exclude build artifacts like dist or temp folders to keep the compiler from getting confused by its own output.

files
Use this only if you have a very small project with just one or two specific files that you want to compile. For 99% of projects, include is the better choice.

Watch Out: If you leave the include array empty, TypeScript will default to including all files in the root directory and subdirectories, which can lead to slow performance.

 

Additional Configuration Options

outDir
This keeps your project clean. Instead of mixing .ts and .js files in the same folder, all compiled code is sent to a specific directory.
Example: "outDir": "./dist".

rootDir
Defines the primary folder where your source code lives. This helps the compiler maintain the folder structure when it outputs files to the outDir.
Example: "rootDir": "./src".

sourceMap
Essential for debugging. It creates .js.map files that allow your browser or IDE to show you the original TypeScript code while you are debugging the running JavaScript.
Example: "sourceMap": true.

declaration
If you are building a library for other developers to use, this generates .d.ts files so the consumers of your library get full autocomplete and type safety.
Example: "declaration": true.

allowJs
Very helpful when migrating a project from JavaScript to TypeScript. It allows you to mix .js and .ts files in the same project during the transition.
Example: "allowJs": true.

 

Using tsconfig.json in Large Projects

In enterprise-scale projects, one size rarely fits all. You might need strict rules for production but looser rules for your unit tests. TypeScript allows you to use the extends property to inherit settings from a base file, keeping your configurations "DRY" (Don't Repeat Yourself).

Example of extending tsconfig.json:

// tsconfig.base.json
{
  "compilerOptions": {
    "target": "ES6",
    "module": "commonjs",
    "strict": true
  }
}
// tsconfig.dev.json
{
  "extends": "./tsconfig.base.json",
  "compilerOptions": {
    "sourceMap": true,             // Enable maps for debugging in dev
    "noUnusedLocals": false        // Allow unused variables while we are still coding
  },
  "include": ["src/**/*.ts"]
}

This inheritance pattern is widely used in monorepos (like those using Nx or Turbo) to maintain a single source of truth for compiler settings.

 

Summary

  • The tsconfig.json file defines how the tsc compiler interacts with your project.
  • The compilerOptions section is where you define things like JavaScript version (target) and type-checking strictness.
  • Use include and exclude with glob patterns to manage which files the compiler "sees."
  • For better debugging, always enable sourceMap in your development environment.
  • For large projects, use the extends feature to share configurations across multiple environments or packages.