Introduction
The tsconfig.json file is the control center of every TypeScript project. It tells the compiler which files to include, how strictly to check types, what JavaScript version to output, and how to resolve modules. Understanding its options is essential for setting up reliable TypeScript projects.
Key Concepts
- tsconfig.json: A JSON configuration file at the root of a TypeScript project that specifies compiler behavior.
- Strict Mode: A master flag that enables all strict type-checking sub-flags at once.
- Target: The ECMAScript version for the compiled JavaScript output.
- Module: The module system used in the output (CommonJS, ESNext, NodeNext).
Real World Context
When you run tsc --init in TypeScript 5.9, it generates a tsconfig with strict: true enabled by default. Every option you set (or leave at default) affects build output, type safety, and compatibility. Getting the configuration right from day one avoids painful migrations later.
Deep Dive
Creating a tsconfig.json
bashnpx tsc --init
This generates a starter configuration. A recommended modern setup:
json{ "compilerOptions": { "target": "ES2022", "module": "NodeNext", "moduleResolution": "NodeNext", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "outDir": "./dist", "rootDir": "./src", "declaration": true, "declarationMap": true, "sourceMap": true }, "include": ["src/**/*"], "exclude": ["node_modules", "dist"] }
Key Options Explained
Type Checking:
strict: Enables all strict sub-flags (covered in the next lesson).noUncheckedIndexedAccess: Makes array/object index access returnT | undefinedinstead ofT.exactOptionalProperties: Distinguishes between missing properties and properties set toundefined.
Emit:
target: The JS version to output.ES2022supports top-level await,Array.at(), and class fields.module: The module system.NodeNextfor Node.js,ESNextfor bundlers.outDir/rootDir: Control where compiled files go and where source files live.declaration: Generate.d.tsfiles for library consumers.sourceMap: Generate.js.mapfiles for debugging.
Module Resolution:
moduleResolution: How TypeScript finds imported files.NodeNextfor Node.js ESM,Bundlerfor Vite/Webpack.paths: Define import aliases like@utils/*.
The include and exclude Fields
json{ "include": ["src/**/*"], "exclude": ["node_modules", "dist", "**/*.test.ts"] }
include specifies which files to compile. exclude removes files from the inclusion set. node_modules is excluded by default.
Common Pitfalls
- Forgetting
moduleResolution— Settingmodulewithout a matchingmoduleResolutionleads to confusing import errors. UseNodeNext/NodeNextorESNext/Bundleras pairs. - Setting target too low —
ES5output dramatically increases bundle size because modern features must be polyfilled. Use the highest target your runtime supports.
Best Practices
- Start with strict: true — Retrofitting strict mode onto an existing project requires fixing hundreds of errors. Enable it from the start.
- Use declaration and declarationMap for libraries — Consumers need
.d.tsfiles for type information and.d.ts.mapfiles for "Go to Definition" to show source code.
Summary
tsconfig.jsoncontrols compilation, type checking, and module resolution.strict: trueenables comprehensive type safety and should always be on.- Match
moduleandmoduleResolutionas a pair (e.g., NodeNext/NodeNext). - Use
declarationandsourceMapfor libraries and debugging.
Code Examples
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"noUncheckedIndexedAccess": true,
"esModuleInterop": true,
"skipLibCheck": true,
"outDir": "./dist",
"rootDir": "./src",
"declaration": true,
"sourceMap": true
},
"include": ["src/**/*"]
}