target controls which version of JavaScript syntax the compiler emits (does a class compile to ES2015 class syntax, or older prototype-based code?), while lib controls which built-in APIs TypeScript assumes are available for type-checking (does Array.prototype.flat exist? does fetch?) — these are independent settings, and picking a lib newer than the actual runtime environment is a real, common source of "it type-checks but crashes at runtime" bugs. paths and baseUrl configure custom import path aliases, and extends lets one tsconfig.json inherit settings from a shared base file, common in monorepos.
target vs lib: two genuinely different settings
Setting lib to a newer JavaScript version than the actual runtime supports means TypeScript will happily type-check code using an API that doesn't exist at runtime yet — a real gap between what compiles and what actually works.
{
"compilerOptions": {
"target": "es2020", // what SYNTAX gets emitted (arrow fns, classes, etc.)
"lib": ["es2020", "dom"] // what APIs TypeScript assumes EXIST (Array methods, fetch, etc.)
}
}
// mismatch example: lib includes "es2023" (Array.prototype.toSorted)
// but the actual deployment target is an older Node.js — compiles fine, crashes at runtimePath aliases with paths and baseUrl
Without this, deeply nested files accumulate long relative import chains (../../../components/Button); a path alias replaces that with a clean, absolute-feeling import that doesn't break when a file moves.
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@components/*": ["src/components/*"],
"@utils/*": ["src/utils/*"]
}
}
}import { Button } from "@components/Button"; // instead of "../../../components/Button"Note that tsc only resolves these aliases for type-checking — a bundler (Vite, webpack) or Node's own resolution needs its own matching alias configuration to actually run the code.
Sharing config with extends
A monorepo commonly has one shared base tsconfig with company-wide settings, and each package extends it with only its own specific overrides — avoiding dozens of near-duplicate config files that could drift out of sync.
{
"compilerOptions": {
"strict": true,
"target": "es2020",
"module": "esnext"
}
}{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "./dist"
},
"include": ["src/**/*"]
}