jsontypescriptconfigjavascript

tsconfig.json Explained: The Compiler Options That Matter

·10 min read·JSON Concepts

The control panel for TypeScript

tsconfig.json tells the TypeScript compiler how to check and build your code: which files to include, how strict to be, what JavaScript version to emit, and how to resolve imports. It is JSON (technically JSONC — comments allowed), and a handful of options account for most of what you will ever change.

json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true
  },
  "include": ["src"]
}

The structure

  • compilerOptions — the bulk of the config; how the compiler behaves.
  • include / exclude — which files are part of the project.
  • extends — inherit from a base config (great for monorepos and shared presets).
  • files — an explicit list when you do not want globbing.

The options that matter most

OptionWhat it controls
targetJavaScript version to emit (ES2022, ESNext).
moduleModule system of the output (ESNext, CommonJS).
moduleResolutionHow imports are found (bundler, node16).
strictMaster switch for all strict type checks.
jsxJSX handling for React (react-jsx).
outDir / rootDirWhere compiled files go / where sources live.
libWhich built-in type definitions to include (DOM, ES2022).

Turn on strict (and leave it on)

"strict": true enables a family of checks that catch real bugs — noImplicitAny, strictNullChecks, and more. It is the single most valuable setting in the file. Starting a new project without it is choosing to find bugs at runtime instead of compile time. On a legacy codebase, enable it and fix errors incrementally rather than leaving it off.

Path aliases

paths (with baseUrl) let you replace fragile relative imports with clean aliases:

json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": { "@/*": ["./src/*"] }
  }
}

Now import { x } from "@/lib/util" works from anywhere. Note that your bundler must understand the same aliases — TypeScript only handles type resolution, not the runtime path rewrite.

Compiler-only vs build settings

A subtlety that confuses people: in many modern setups (Next.js, Vite), a bundler — not tsc — actually builds your code. There, TypeScript is used mainly for type checking, and options like noEmit: true tell it not to output files. Settings like target and module may be overridden by the bundler. Know who is doing the compiling in your stack.

A sensible modern baseline

json
{
  "compilerOptions": {
    "target": "ES2022",
    "lib": ["ES2022", "DOM"],
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "noEmit": true,
    "paths": { "@/*": ["./src/*"] },
    "baseUrl": "."
  },
  "include": ["src"]
}

Frequently asked questions

It turns on a group of strict type-checking flags (like strictNullChecks and noImplicitAny) that catch a large class of bugs at compile time. Keep it on for new projects.

TypeScript paths only affect type resolution. Your bundler or runtime must be configured with the same aliases (for example via a plugin or its own config) to rewrite them when the code actually runs.

Yes — it is JSONC, so // and /* */ comments are allowed even though standard JSON forbids them. Many editors read it to power autocomplete.

Try JSON Validator

Check your tsconfig.json for syntax errors instantly.