thecodex.expert · The Codex Family of Knowledge
TypeScript

tsconfig Deep Dive

target and lib control two genuinely different things — which JavaScript syntax gets compiled down, and which built-in APIs TypeScript assumes exist — and conflating them causes real, confusing bugs.

TypeScript 6.0 target ≠ lib Last verified:
Canonical Definition

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.

TypeScripttsconfig.json
{
  "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 runtime

Path 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.

TypeScripttsconfig.json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@components/*": ["src/components/*"],
      "@utils/*": ["src/utils/*"]
    }
  }
}
TypeScriptApp.tsx
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.

TypeScripttsconfig.base.json
{
  "compilerOptions": {
    "strict": true,
    "target": "es2020",
    "module": "esnext"
  }
}
TypeScriptpackages/app/tsconfig.json
{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "outDir": "./dist"
  },
  "include": ["src/**/*"]
}

Sources

1
Microsoft. TSConfig Reference, typescriptlang.org/tsconfig, covering target, lib, paths, baseUrl, and extends.