Tsconfig
TypeScript compiler configuration, project references, and tsconfig best practices
You are an expert in TypeScript compiler configuration, tsconfig.json authoring, and project structuring for JavaScript and TypeScript codebases. ## Key Points - Enable `strict: true` on every project. It enables a collection of important checks including `strictNullChecks`, `noImplicitAny`, and `strictFunctionTypes`. - Use `noUncheckedIndexedAccess` to force handling of possibly-undefined values from array and record indexing. - Set `verbatimModuleSyntax` (TypeScript 5.0+) to enforce explicit `import type` for type-only imports. This ensures bundlers can reliably tree-shake types. - Use `moduleResolution: "bundler"` for projects built with Vite, Webpack, or esbuild. Use `"Node16"` for pure Node.js projects. - Set `isolatedModules: true` to ensure compatibility with single-file transpilers (esbuild, SWC, Babel) that cannot perform cross-file type analysis. - Use `skipLibCheck: true` to speed up compilation by not type-checking declaration files in `node_modules`. - Use project references with `composite: true` for monorepos to enable incremental builds with `tsc --build`. - **Path aliases not resolved at runtime**: TypeScript `paths` only affect type resolution. You need bundler aliases or a tool like `tsc-alias` to rewrite imports in emitted JavaScript. - **moduleResolution mismatch**: Using `"bundler"` resolution with a Node.js runtime (without a bundler) will cause resolution failures. Node.js requires `"Node16"` or `"NodeNext"`. - **Missing include/exclude**: Without `include`, TypeScript processes all `.ts` files in the directory tree, including `node_modules` or generated files. Always set `include` explicitly. - **composite without declaration**: Project references require `composite: true`, which implicitly enables `declaration`. If you set `declaration: false` in a composite project, it will error.
skilldb get build-tools-skills/TsconfigFull skill: 240 linesTypeScript Compiler Configuration — Build Tools
You are an expert in TypeScript compiler configuration, tsconfig.json authoring, and project structuring for JavaScript and TypeScript codebases.
Core Philosophy
Overview
The TypeScript compiler (tsc) uses tsconfig.json to control type checking, module resolution, output targets, and project structure. Proper configuration is essential for type safety, build performance, and compatibility with bundlers. TypeScript 5.x introduced verbatimModuleSyntax, bundler module resolution, and configuration inheritance improvements.
Setup & Configuration
Recommended Modern tsconfig.json
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"jsx": "react-jsx",
"strict": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true,
"forceConsistentCasingInFileNames": true,
"verbatimModuleSyntax": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"skipLibCheck": true,
"esModuleInterop": true,
"resolveJsonModule": true,
"isolatedModules": true,
"outDir": "./dist",
"rootDir": "./src",
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}
Node.js Backend tsconfig.json
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"lib": ["ES2022"],
"strict": true,
"noUncheckedIndexedAccess": true,
"verbatimModuleSyntax": true,
"declaration": true,
"sourceMap": true,
"outDir": "./dist",
"rootDir": "./src",
"skipLibCheck": true,
"resolveJsonModule": true,
"isolatedModules": true
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}
Library tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "bundler",
"lib": ["ES2020"],
"strict": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"outDir": "./dist",
"rootDir": "./src",
"skipLibCheck": true,
"isolatedModules": true,
"verbatimModuleSyntax": true
},
"include": ["src"],
"exclude": ["node_modules", "dist", "**/*.test.ts"]
}
Core Patterns
Configuration Inheritance
// tsconfig.base.json
{
"compilerOptions": {
"strict": true,
"target": "ES2022",
"moduleResolution": "bundler",
"skipLibCheck": true,
"isolatedModules": true,
"verbatimModuleSyntax": true
}
}
// tsconfig.json
{
"extends": "./tsconfig.base.json",
"compilerOptions": {
"module": "ESNext",
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src"]
}
Project References (Monorepo)
// tsconfig.json (root)
{
"files": [],
"references": [
{ "path": "./packages/core" },
{ "path": "./packages/ui" },
{ "path": "./packages/server" }
]
}
// packages/core/tsconfig.json
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"composite": true,
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src"]
}
// packages/ui/tsconfig.json
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"composite": true,
"outDir": "./dist",
"rootDir": "./src"
},
"references": [{ "path": "../core" }],
"include": ["src"]
}
Build with: tsc --build (or tsc -b) to incrementally compile referenced projects.
Separate Config for Tests
// tsconfig.test.json
{
"extends": "./tsconfig.json",
"compilerOptions": {
"noEmit": true,
"types": ["vitest/globals"]
},
"include": ["src", "tests"]
}
Path Aliases with Bundler Support
// tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"],
"@components/*": ["./src/components/*"],
"@utils/*": ["./src/utils/*"]
}
}
}
Note: paths only tells TypeScript how to resolve types. Your bundler (Vite, Webpack, etc.) must also be configured with matching aliases.
Best Practices
- Enable
strict: trueon every project. It enables a collection of important checks includingstrictNullChecks,noImplicitAny, andstrictFunctionTypes. - Use
noUncheckedIndexedAccessto force handling of possibly-undefined values from array and record indexing. - Set
verbatimModuleSyntax(TypeScript 5.0+) to enforce explicitimport typefor type-only imports. This ensures bundlers can reliably tree-shake types. - Use
moduleResolution: "bundler"for projects built with Vite, Webpack, or esbuild. Use"Node16"for pure Node.js projects. - Set
isolatedModules: trueto ensure compatibility with single-file transpilers (esbuild, SWC, Babel) that cannot perform cross-file type analysis. - Use
skipLibCheck: trueto speed up compilation by not type-checking declaration files innode_modules. - Use project references with
composite: truefor monorepos to enable incremental builds withtsc --build.
Common Pitfalls
- Path aliases not resolved at runtime: TypeScript
pathsonly affect type resolution. You need bundler aliases or a tool liketsc-aliasto rewrite imports in emitted JavaScript. - moduleResolution mismatch: Using
"bundler"resolution with a Node.js runtime (without a bundler) will cause resolution failures. Node.js requires"Node16"or"NodeNext". - Missing include/exclude: Without
include, TypeScript processes all.tsfiles in the directory tree, includingnode_modulesor generated files. Always setincludeexplicitly. - esModuleInterop confusion: This flag adds helpers for importing CommonJS modules with default import syntax. It is needed for
import express from 'express'to work but adds a small runtime overhead. WithverbatimModuleSyntax, you may need to useimport * as express from 'express'instead. - composite without declaration: Project references require
composite: true, which implicitly enablesdeclaration. If you setdeclaration: falsein a composite project, it will error. - Stale build output: When using
tsc --build, incremental builds may use stale.tsbuildinfofiles. Deletedist/and.tsbuildinfofiles when encountering unexplainable type errors after config changes.
Anti-Patterns
Over-engineering for hypothetical scale. Building for millions of users when you have hundreds adds complexity without value. Solve today's problems first.
Ignoring the existing ecosystem. Reinventing functionality that mature libraries already provide well wastes time and introduces unnecessary risk.
Premature abstraction. Creating elaborate frameworks and utilities before you have enough concrete cases to know what the abstraction should look like produces the wrong abstraction.
Neglecting error handling at boundaries. Internal code can trust its inputs, but system boundaries (user input, APIs, file I/O) require defensive validation.
Skipping documentation for obvious code. What is obvious to you today will not be obvious to your colleague next month or to you next year.
Install this skill directly: skilldb add build-tools-skills
Related Skills
Biome
Biome linter, formatter, and code analysis configuration for JavaScript and TypeScript projects
Esbuild
esbuild bundling, transpilation, and build scripting for ultra-fast JavaScript builds
Eslint Prettier
ESLint and Prettier combined setup for JavaScript and TypeScript linting and formatting
Rollup
Rollup configuration for building JavaScript libraries with tree shaking and multiple output formats
Swc
SWC compiler configuration for fast TypeScript and JavaScript transpilation and minification
Vite
Vite configuration, plugins, and dev server optimization for modern JavaScript projects