Quick answer: tsconfig.json tells the compiler which files to include and how strict to be. For a new project, start from tsc --init and keep strict on. The options that matter most are target, module, moduleResolution, strict, verbatimModuleSyntax and skipLibCheck. Current TypeScript deprecates target: es5 and moduleResolution: node10.

A modern starting point

{
  "compilerOptions": {
    "target": "es2022",
    "module": "preserve",
    "moduleResolution": "bundler",
    "strict": true,
    "verbatimModuleSyntax": true,
    "skipLibCheck": true,
    "noUncheckedIndexedAccess": true,
    "noEmit": true
  },
  "include": ["src"]
}
OptionWhat it does
targetThe JavaScript version emitted — es2022 or later today
moduleThe module syntax; preserve for bundlers, nodenext for Node
moduleResolutionHow imports are resolved; bundler for bundled apps
strictTurns on the whole family of strict checks
verbatimModuleSyntaxHonours import type exactly as written
noEmitType-check only; let the bundler emit JavaScript

Choosing module settings by project type

There is no single correct pair. The right answer depends on how the code runs.

// bundled browser app (Vite, Angular, Next.js)
{ "module": "preserve", "moduleResolution": "bundler" }

// Node.js app or library
{ "module": "nodenext", "moduleResolution": "nodenext" }

bundler lets you write extensionless imports the way a bundler expects. nodenext models Node’s real rules, including that ESM imports need a file extension. Picking the wrong one produces a flood of “cannot find module” errors that look like a broken install but are really a configuration mismatch.

What current TypeScript removed or deprecated

  • target: "es5" and downlevelIteration — deprecated, and unsupported in TypeScript 7. Target ES2022 or later.
  • moduleResolution: "node" (node10) — deprecated in favour of bundler or nodenext.
  • baseUrl — deprecated; write paths relative to the config file instead.
  • module: "amd" | "umd" | "systemjs" | "none" — no longer supported. Use a bundler for bundling.
  • import x from "./f" assert { type: "json" } — the asserts keyword is replaced by with.

If your config contains any of these, expect errors after upgrading. They are migration items, not bugs.

strict is not one option, it is a family

strict: true switches on several checks at once, most importantly strictNullChecks, which is what makes undefined a type you must handle. Two additions beyond the preset are worth turning on:

{
  "compilerOptions": {
    "strict": true,
    "noUncheckedIndexedAccess": true,   // arr[0] is T | undefined
    "noImplicitOverride": true,         // require the override keyword
    "exactOptionalPropertyTypes": true  // distinguish missing from undefined
  }
}

noUncheckedIndexedAccess is the strictest of the three and the one people turn off first, because it forces a check before using any indexed access. On a new project, leave it on: it prevents the most common runtime crash in JavaScript, reading a property of undefined.

Project references for large codebases

For a monorepo, split the config into a base file and per-package files that extend it, then use references so the compiler builds packages in dependency order and caches results.

// tsconfig.json
{ "extends": "./tsconfig.base.json",
  "references": [{ "path": "./packages/ui" }] }

// packages/ui/tsconfig.json
{ "extends": "../../tsconfig.base.json",
  "compilerOptions": { "composite": true, "outDir": "dist" } }

That completes the TypeScript track. To learn TypeScript and Angular properly with a mentor, Ampersand Academy teaches web development one-to-one.

Frequently asked questions

What is the most important tsconfig option?

strict, because it enables the whole family of strict checks, including treating null and undefined as types you must handle. Everything else is tuning around that.

Should I use moduleResolution bundler or node?

Use bundler for an app built by a bundler such as Vite or Angular, and nodenext for a Node.js project. The old node setting is deprecated.

What is verbatimModuleSyntax?

It makes the compiler honour your import and export statements exactly, including import type, so type-only imports are erased reliably and never leak into the bundle.

Why does my tsconfig throw errors after upgrading TypeScript?

Recent versions deprecated options such as target es5, downlevelIteration, moduleResolution node and baseUrl. Update the config to the current equivalents, such as es2022 and bundler.

What is noUncheckedIndexedAccess?

It makes indexed access return a possibly undefined type, so arr[0] must be checked before use. It prevents the most common runtime crash, reading a property of undefined.

Last updated on · Written by