Skip to content
Laviyx Learn

Try

TypeScript course menu

tsconfig.json Explained: strict Mode and the Compiler Options That Matter

A practical guide to tsconfig.json: what strict mode enables, target and lib, module and moduleResolution, outDir and rootDir, noEmit, extra safety options like noUncheckedIndexedAccess, extends, include and exclude, and recommended configurations for Node.js and front-end projects.

Advanced7 min readUpdated
On this page

tsconfig.json tells the TypeScript compiler which files belong to the project and how to check and compile them. There are over a hundred options, but a handful matter for almost every project.

A modern starting point#

tsconfig.json (Node.js project) (JSON)
{
  "compilerOptions": {
    "target": "es2022",
    "lib": ["es2023"],
    "module": "nodenext",
    "moduleResolution": "nodenext",
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "verbatimModuleSyntax": true,
    "skipLibCheck": true,
    "outDir": "dist",
    "rootDir": "src",
    "sourceMap": true
  },
  "include": ["src"]
}

For a front-end project built with a bundler (Vite, Next.js), typical differences are "lib": ["es2023", "dom", "dom.iterable"], "moduleResolution": "bundler", "jsx": "react-jsx" for React, and "noEmit": true (the bundler produces the JavaScript; tsc only type-checks).

strict#

"strict": true switches on a family of checks. It should be on in every new project:

Option (enabled by strict)Catches
strictNullChecksUsing values that may be null or undefined
noImplicitAnyParameters and variables whose type silently becomes any
strictFunctionTypesUnsafe callback parameter types
strictPropertyInitializationClass properties that are never initialised
strictBindCallApplyWrong arguments to bind, call and apply
useUnknownInCatchVariablesTreats catch (error) as unknown instead of any
alwaysStrictEmits JavaScript strict mode
noImplicitThisthis with an implicit any type

Without strict mode, this compiles; with it, TypeScript catches the bug:

TypeScript
function greet(name) {
  return "Hello, " + name.toUpperCase();
}
Compiler error
error TS7006: Parameter 'name' implicitly has an 'any' type.

Migrating an old JavaScript project? Turn options on one at a time, but aim for full strict mode.

Extra safety options#

These are not part of strict but are worth considering:

OptionEffect
noUncheckedIndexedAccessarray[i] and record[key] include undefined, forcing a check
exactOptionalPropertyTypesprop?: string cannot be set to undefined explicitly
noImplicitOverrideRequires override on methods that replace a parent's method
noFallthroughCasesInSwitchReports switch cases that fall through without break
noUnusedLocals, noUnusedParametersReports unused variables and parameters (often left to a linter instead)

noUncheckedIndexedAccess in action:

TypeScript
// with "noUncheckedIndexedAccess": true
const scores: number[] = [72, 95];
const first: number = scores[0];
Compiler error
error TS2322: Type 'number | undefined' is not assignable to type 'number'.

(The examples on this site are checked with strict; this block also enables noUncheckedIndexedAccess to show its effect.)

target and lib#

  • target sets which JavaScript version is output. Syntax newer than the target is converted (for example, optional chaining to older checks). For modern browsers and Node.js, es2022 or later is typical.
  • lib sets which built-in APIs TypeScript knows about. es2023 adds types for features like toSorted; dom adds document, window and HTMLElement. If TypeScript says a built-in method "does not exist", your lib may be too old.

Note that lib only provides types; it does not add the feature to old browsers.

module and moduleResolution#

ProjectSettings
Node.js (ES modules)"module": "nodenext", "moduleResolution": "nodenext", relative imports end in .js
Bundler (Vite, webpack, Next.js)"module": "esnext", "moduleResolution": "bundler"

See Modules and Imports.

Output options#

OptionEffect
outDirFolder for compiled JavaScript, such as dist
rootDirFolder containing the source, such as src (keeps the folder structure in outDir)
noEmitType-check only, write nothing
sourceMapGenerate source maps so errors point at .ts lines
declarationGenerate .d.ts files (for libraries)

include, exclude and extends#

  • include lists the files or folders to compile (["src"]); exclude removes some (such as test fixtures). node_modules is excluded by default.
  • extends reuses another configuration. Shared base configs are published on npm, for example in the @tsconfig/ scope:
JSON
{
  "extends": "@tsconfig/node22/tsconfig.json",
  "compilerOptions": { "outDir": "dist" }
}

How the examples in this course are checked#

The code in this course is checked with --strict, target ES2022, the es2023 and dom libraries, nodenext modules and skipLibCheck, and @types/node for examples that use Node.js modules.

Practice#

Practice exercise

A React front end is built with Vite. Write a tsconfig.json with strict checking, safe indexed access, DOM types, bundler module resolution, the React JSX transform, and no JavaScript output from tsc, including only the src folder.

Show solution
tsconfig.json (JSON)
{
  "compilerOptions": {
    "target": "es2022",
    "lib": ["es2023", "dom", "dom.iterable"],
    "module": "esnext",
    "moduleResolution": "bundler",
    "jsx": "react-jsx",
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "verbatimModuleSyntax": true,
    "skipLibCheck": true,
    "noEmit": true
  },
  "include": ["src"]
}

Vite compiles the TypeScript for the browser; npx tsc (or a typecheck script) only checks types.

Key takeaway#

Finished this lesson?