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
| Option | What It Enforces | Example Error Caught |
|---|---|---|
noImplicitAny | All variables must have explicit or inferred types | function greet(name) — name has implicit any |
strictNullChecks | null/undefined not assignable to other types | let name: string = null — error |
noUnusedLocals | Report unused local variables | Declaring a variable and never using it |
noUnusedParameters | Report unused function parameters | A function parameter that is never used |
noImplicitReturns | All code paths must return a value | Function missing a return in one branch |
exactOptionalPropertyTypes | Optional props cannot be set to undefined explicitly | Setting 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 Type | Key Settings |
|---|---|
| Node.js Backend | target: "ES2022", module: "NodeNext", moduleResolution: "NodeNext" |
| Browser App (bundled) | target: "ES2020", module: "ESNext", moduleResolution: "Bundler" |
| React App | jsx: "react-jsx", strict: true, esModuleInterop: true |
| Library / Package | declaration: 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
| Option | Purpose | Recommended Value |
|---|---|---|
target | JS version to output | ES2020 |
strict | Enable all strict checks | true |
outDir | Compiled output directory | ./dist |
rootDir | TypeScript source root | ./src |
sourceMap | Enable debugging maps | true |
declaration | Generate .d.ts files | true for libraries |
esModuleInterop | Better module compatibility | true |
resolveJsonModule | Import JSON files | true |
noUnusedLocals | Warn on unused variables | true |
noImplicitReturns | All paths must return | true |
