tsconfig cheatsheet for 2026.
Minimum modern tsconfig
{
"compilerOptions": {
"target": "ES2023",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"lib": ["ES2023"],
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"isolatedModules": true,
"verbatimModuleSyntax": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true,
"outDir": "dist",
"rootDir": "src"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
strict family
"strict": true enables:
noImplicitAnystrictNullChecksstrictFunctionTypesstrictBindCallApplystrictPropertyInitializationnoImplicitThisalwaysStrictuseUnknownInCatchVariables
Always start with strict.
Extra checks worth enabling
{
"noUncheckedIndexedAccess": true, // arr[0] is T | undefined
"noImplicitOverride": true, // require `override` keyword
"noFallthroughCasesInSwitch": true,
"noPropertyAccessFromIndexSignature": true,
"exactOptionalPropertyTypes": true,
"noUnusedLocals": true,
"noUnusedParameters": true
}
target vs lib
target: JS version the emitted output uses (ES2023, etc).lib: which built-in type declarations to include (DOM, ES2023, etc).
For Node:
{ "target": "ES2023", "lib": ["ES2023"] }
For browser:
{ "target": "ES2022", "lib": ["ES2022", "DOM", "DOM.Iterable"] }
module / moduleResolution (the confusing pair)
For pure Node ESM/CJS (2026):
{ "module": "NodeNext", "moduleResolution": "NodeNext" }
For bundlers (Vite/webpack/esbuild):
{ "module": "ESNext", "moduleResolution": "bundler" }
Never use "node" or "node16" for new projects — use NodeNext or bundler.
verbatimModuleSyntax
import type { User } from "./types";
import { fn } from "./mod";
Requires you to mark type-only imports. Clean and avoids round-tripping issues with ESM.
isolatedModules
Required for tools like swc/esbuild that compile files independently. Prevents const enum, untagged re-exports of types, etc.
Path aliases
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@lib/*": ["src/lib/*"]
}
}
}
import { foo } from "@/lib/foo";
Paths only affect type checking. Runtime needs tsconfig-paths, bundler config, or output rewriting.
Project references (monorepo)
// tsconfig.json
{
"files": [],
"references": [
{ "path": "packages/core" },
{ "path": "packages/web" }
]
}
// packages/core/tsconfig.json
{
"compilerOptions": {
"composite": true,
"declaration": true,
"outDir": "dist"
}
}
tsc --build # build all, in dependency order
tsc --build --watch
extends
{
"extends": "@tsconfig/node20/tsconfig.json",
"compilerOptions": {
"outDir": "dist"
}
}
Use community @tsconfig/* presets when you don’t want to manage from scratch.
JSX
{
"jsx": "react-jsx", // React 17+ classic transform
"jsx": "preserve" // for downstream bundler
}
Source maps
{
"sourceMap": true,
"declarationMap": true,
"inlineSources": true
}
Always include sourcemaps in dev; toggle for prod.
Declaration files
{
"declaration": true, // emit .d.ts
"emitDeclarationOnly": true, // ONLY emit types (let esbuild emit JS)
"declarationMap": true
}
Output options
{
"removeComments": false,
"newLine": "lf",
"preserveConstEnums": true,
"importHelpers": true // use tslib helpers
}
Files / include / exclude
{
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "**/*.test.ts"],
"files": [] // explicit list (rarely needed)
}
Common gotchas
target: ES5is no longer needed — browsers ship ES2022.module: CommonJSwith ESM dependencies — interop nightmare.strict: false“to make it work” — pay the type debt later instead.skipLibCheck: false— slow CI; almost always want true.pathswithout bundler config — works at type-check, fails at runtime.
CLI
tsc # build with tsconfig.json
tsc --noEmit # type-check only
tsc --watch
tsc --build packages/core
tsc --showConfig # print resolved config
Read this next
If you want my strict tsconfig templates (lib, web, node), they’re at rajpoot.dev .
Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .