tsconfig.json and Compiler Options
|
This section documents the current TypeScript release line as published at the official TypeScript documentation, which is the reference these pages are written and verified against. No specific patch version is pinned. This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production, since TypeScript iterates quickly. This section’s bibliography lists the reference material consulted while preparing these pages. |
tsc reads its settings from a tsconfig.json file at the project root. That file marks the directory as
the project; running tsc with no input files then compiles every file the config selects. The three
companion references are the tsconfig reference (every option, one
page), the tsconfig.json handbook (how the
file works), and the compiler options
handbook (the CLI flag list).
tsc --init and the config file
tsc --init writes a commented starter tsconfig.json:
npm install --save-dev typescript
npx tsc --init # writes ./tsconfig.json
npx tsc # compile the project described by ./tsconfig.json
npx tsc -p ./packages/api # compile using a specific tsconfig
Almost every option has a matching CLI flag (tsc --target es2022 --strict). Flags passed on the command
line override the file for that run, but a project build (tsc -p, or a bare tsc that finds a
tsconfig.json) ignores individual file arguments — you cannot mix tsc file.ts with a project. Use flags
for one-off runs; use the file for anything repeatable.
files, include, exclude
{
"compilerOptions": { "outDir": "dist" },
"files": ["src/main.ts"],
"include": ["src/**/*", "types/**/*.d.ts"],
"exclude": ["node_modules", "dist", "**/*.test.ts"]
}
files is an explicit list (no globs). include / exclude take globs; exclude only removes entries
that include added, and defaults to node_modules, bower_components, jspm_packages, and the outDir.
If neither files nor include is present, the compiler takes every .ts / .tsx / .d.ts file under
the config directory. A file pulled in by an import or a /// <reference> is compiled even when no
pattern lists it.
extends and shared config bases
extends merges another config into this one; the deriving file wins on conflicts. Relative paths such as
outDir in the base resolve relative to the base file, not the deriving one.
npm install --save-dev @tsconfig/node20 @tsconfig/strictest
{
"extends": ["@tsconfig/node20/tsconfig.json", "@tsconfig/strictest/tsconfig.json"],
"compilerOptions": {
"outDir": "dist",
"rootDir": "src"
},
"include": ["src"]
}
The community @tsconfig/* bases package a runtime target
(@tsconfig/node20, @tsconfig/deno, @tsconfig/create-react-app) or a strictness level
(@tsconfig/recommended, @tsconfig/strictest) so each project only writes what is genuinely
project-specific. extends accepts a single string or an array; with an array, later entries win.
The ${configDir} substitution
Inside a base config, ${configDir} (TypeScript 5.5+) expands to the directory of the leaf project that
is loading the base, not the directory of the base itself. It lets one shared base name paths that resolve
against each consumer.
// tsconfig.base.json -- shared across a monorepo
{
"compilerOptions": {
"outDir": "${configDir}/dist",
"rootDir": "${configDir}/src",
"tsBuildInfoFile": "${configDir}/dist/.tsbuildinfo",
"typeRoots": ["${configDir}/node_modules/@types", "${configDir}/types"]
}
}
It is written ${configDir} in this prose only to stop AsciiDoc from treating it as an attribute
reference; in the JSON file it is literal. Without it, a relative outDir in a base always resolves next to
the base file, so every project sharing the base would emit into one folder.
strict and its members
strict is a bundle: turning it on enables every flag in the table below, and keeps enabling newly added
ones in future releases. Turn the bundle on, then switch individual members off only with a reason. See
strict.
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
"noImplicitOverride": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
}
}
Member enabled by strict |
Effect |
|---|---|
Error when a parameter or variable is implicitly |
|
|
|
Function-type parameter positions are checked contravariantly (method parameters excepted). |
|
|
|
Class fields must be assigned in the constructor, or declared optional or with |
|
The |
|
Error on a |
|
Parse every file in strict mode and emit |
The linting-adjacent flags are not part of strict and are set on their own:
| Flag | Effect |
|---|---|
Error on an unused local variable. |
|
Error on an unused parameter; prefix the name with |
|
Every code path in a value-returning function must return. |
|
A non-empty |
|
Index and element access add |
|
|
|
A method that overrides a base method must carry the |
Emit options
These control what JavaScript tsc writes and how it is downleveled; the full list is on the
tsconfig reference.
{
"compilerOptions": {
"target": "es2022",
"lib": ["es2022", "dom", "dom.iterable"],
"module": "nodenext",
"moduleResolution": "nodenext",
"rootDir": "src",
"outDir": "dist",
"declaration": true,
"sourceMap": true,
// "inlineSourceMap": true, // folds the map into the .js; cannot combine with sourceMap
"removeComments": false,
"downlevelIteration": true,
"importHelpers": true, // emit helpers as tslib imports; run: npm i tslib
"jsx": "react-jsx",
"verbatimModuleSyntax": true,
"isolatedModules": true,
"noEmit": false,
"noEmitOnError": true
}
}
-
targetsets the output ECMAScript level and the defaultlib.liboverrides which built-in API declarations are in scope — adddomfor browser globals. -
modulepicks the output module format;moduleResolutionpicks how bare specifiers are looked up.nodenext/node16follow Node’s ESM rules and want extensions on relative imports;bundlerfits Vite / esbuild / webpack. Depth in Modules. -
outDir/rootDirplace emitted files.rootDirpins the base folder so the tree underoutDirmirrors the sources. -
declarationemits.d.ts;sourceMapemits.js.map;inlineSourceMapfolds the map into the.jsand is mutually exclusive withsourceMap. -
noEmittype-checks only, leaving output to a bundler;noEmitOnErrorwithholds all output when any error is found. -
removeCommentsstrips comments from output.downlevelIterationmakesfor…ofand spread correct on arbitrary iterables whentargetis below ES2015, at the cost of larger output.importHelperspulls__extends-style helpers fromtslibinstead of inlining them into every file. -
jsxselects JSX handling —react-jsxfor the modern runtime,preserveto leave it for a bundler; see TypeScript with React. -
verbatimModuleSyntaxdrops anyimport/exportthat referenced only types and leaves the rest exactly as written, soimport typebecomes mandatory for type-only imports.isolatedModulesrestricts you to syntax a single-file transpiler (Babel, esbuild) can process.
For single-file transpilers, TypeScript 5.8 adds the --erasableSyntaxOnly flag: it bans runtime-only
constructs (enum, value-bearing namespace, parameter properties, import =) so each file is valid
"types-as-comments" JavaScript.
npx tsc --erasableSyntaxOnly --noEmit
Type-checking configuration
{
"compilerOptions": {
"skipLibCheck": true,
"types": ["node", "vitest/globals"],
"typeRoots": ["./node_modules/@types", "./types"],
"allowJs": true,
"checkJs": false
}
}
-
skipLibCheckskips type-checking of.d.tsfiles, including those in dependencies — a large speed-up and the common default.typesrestricts the auto-included@types/packages to just the listed names (otherwise every package undernode_modules/@typesis global).typeRootschanges *where ambient type packages are searched. -
allowJslets.jsfiles join the program and be emitted;checkJsadditionally type-checks them, reading JSDoc as annotations — the usual first step of a migration, also covered in TypeScript with React.
Watch mode and inspection flags
npx tsc --watch # rebuild on change; alias -w
npx tsc --watch --preserveWatchOutput
npx tsc --showConfig # print the fully resolved config (after extends) and exit
npx tsc --explainFiles # list every file in the program and why it was included
npx tsc --noEmit --pretty false # plain, uncoloured diagnostics for CI
--showConfig is how you see what extends and ${configDir} actually resolved to. --explainFiles
answers "why is this file being compiled?". --pretty is on by default and controls colour and formatting
of diagnostics. Under --watch, assumeChangesOnlyAffectDirectlyDependentFiles makes a rebuild re-check
only direct importers instead of walking the whole dependency graph — faster, occasionally less accurate.
{
"compilerOptions": {
"assumeChangesOnlyAffectDirectlyDependentFiles": true
},
"watchOptions": {
"watchFile": "useFsEvents",
"excludeDirectories": ["**/node_modules", "dist"]
}
}
See also
-
tsconfig reference — every option, with examples.
-
tsconfig.json handbook —
files/include/excluderesolution andextends. -
Compiler options handbook — the CLI flags and how they map to the file.
-
Modules —
module,moduleResolution, and import rules in depth. -
Project References and Build —
composite,tsc --build, and the multi-project layouts that lean onextendsand${configDir}. -
TypeScript with React and TypeScript Essentials for Angular — the framework CLIs ship their own tuned
tsconfig.json; those pages describe the preset each one generates.