Episode 1 — Fundamentals / 1.25 — TypeScript Essentials
1.25.j — tsconfig.json Explained
In one sentence:
tsconfig.jsonis the configuration file for the TypeScript compiler — it controls what JavaScript version to target, which module system to use, how strict the type checking is, and which files to include or exclude.
Navigation: ← 1.25 Overview · 1.25.k — TypeScript Compiler →
1. What tsconfig.json does
tsconfig.json sits at the root of your TypeScript project and tells the compiler (tsc):
- What to compile — which files to include/exclude
- How to compile — target JavaScript version, module system, output directory
- How strict to be — which type-checking rules to enforce
- Where to put output — compiled JavaScript destination
Without tsconfig.json, you must pass all options as CLI flags. With it, just run npx tsc.
2. Creating tsconfig.json
npx tsc --init
This generates a tsconfig.json with many options commented out and sensible defaults. The generated file includes explanatory comments for each option.
3. Key compiler options explained
target — JavaScript output version
Determines which JS features are available in the output:
{
"compilerOptions": {
"target": "ES2020"
}
}
| Value | Output includes | Use when |
|---|---|---|
"ES5" | No arrow functions, no let/const, no template literals | Supporting very old browsers |
"ES2015" / "ES6" | Arrow functions, let/const, classes, promises | Broad browser support |
"ES2020" | Optional chaining (?.), nullish coalescing (??), BigInt | Recommended for most projects |
"ES2022" | Top-level await, Array.at(), class fields | Modern environments |
"ESNext" | Latest features — changes with each TS release | When using a bundler that handles downleveling |
Rule of thumb: Match your target to your deployment environment's minimum supported JavaScript version.
module — module system
Controls how import/export statements are compiled:
{
"compilerOptions": {
"module": "commonjs"
}
}
| Value | Output style | Use when |
|---|---|---|
"commonjs" | require() / module.exports | Node.js (traditional) |
"ESNext" / "ES2020" | import / export (kept as-is) | Bundlers (Vite, webpack, Rollup) |
"NodeNext" | Smart — CommonJS or ESM based on .mts/.cts extension | Node.js with ESM support |
strict — enable all strict checks
{
"compilerOptions": {
"strict": true
}
}
strict: true enables ALL of these:
| Flag | What it does |
|---|---|
strictNullChecks | null/undefined not assignable to other types |
noImplicitAny | Error when TypeScript cannot infer a type (falls to any) |
strictFunctionTypes | Stricter function parameter checking |
strictBindCallApply | Check bind, call, apply argument types |
strictPropertyInitialization | Class properties must be initialized |
noImplicitThis | Error when this has implicit any type |
alwaysStrict | Emits "use strict" in every file |
useUnknownInCatchVariables | catch(e) → e is unknown instead of any |
Always use strict: true for new projects. It catches the most bugs.
outDir — compiled output directory
{
"compilerOptions": {
"outDir": "./dist"
}
}
Compiled .js files go into ./dist/ instead of alongside source .ts files.
rootDir — source root directory
{
"compilerOptions": {
"rootDir": "./src"
}
}
Tells the compiler where your source files live. This affects the output directory structure:
src/index.ts → dist/index.js (not dist/src/index.js)
src/utils/a.ts → dist/utils/a.js
lib — built-in type declarations
Controls which built-in API types are available:
{
"compilerOptions": {
"lib": ["ES2020", "DOM", "DOM.Iterable"]
}
}
| Value | Includes types for |
|---|---|
"ES2020" | Promise, Map, Set, Array.from, etc. |
"DOM" | document, window, HTMLElement, fetch, etc. |
"DOM.Iterable" | NodeList.forEach, iterable DOM collections |
"WebWorker" | Web Worker APIs |
For Node.js projects (no browser): omit "DOM" and install @types/node instead.
jsx — React JSX handling
{
"compilerOptions": {
"jsx": "react-jsx"
}
}
| Value | Behavior |
|---|---|
"react-jsx" | Modern React 17+ automatic JSX transform (no import React needed) |
"react" | Classic: React.createElement (requires import React) |
"preserve" | Keep JSX as-is — let the bundler handle it |
moduleResolution — how imports are resolved
{
"compilerOptions": {
"moduleResolution": "bundler"
}
}
| Value | Use when |
|---|---|
"node" | Classic Node.js resolution (node_modules lookup) |
"node16" / "nodenext" | Modern Node.js with ESM support |
"bundler" | Using Vite, webpack, esbuild (recommended for bundled projects) |
esModuleInterop — import compatibility
{
"compilerOptions": {
"esModuleInterop": true
}
}
Enables import express from 'express' instead of import * as express from 'express'. Almost always set to true.
skipLibCheck — faster compilation
{
"compilerOptions": {
"skipLibCheck": true
}
}
Skips type-checking of .d.ts declaration files (from node_modules/@types). Speeds up compilation significantly. Recommended: true — your own code is still checked.
include / exclude — file selection
{
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "**/*.test.ts"]
}
| Property | Purpose |
|---|---|
include | Glob patterns for files to compile |
exclude | Glob patterns for files to skip |
files | Explicit list of files (rarely used) |
4. Common configurations
Minimal configuration
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"strict": true,
"outDir": "./dist",
"rootDir": "./src",
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
Node.js project
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"lib": ["ES2020"],
"strict": true,
"outDir": "./dist",
"rootDir": "./src",
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
React project (Vite)
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"strict": true,
"jsx": "react-jsx",
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"noEmit": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"isolatedModules": true
},
"include": ["src"],
"exclude": ["node_modules"]
}
Key differences for React:
"jsx": "react-jsx"— JSX support"noEmit": true— Vite handles compilation; tsc only type-checks"moduleResolution": "bundler"— let Vite resolve imports"lib"includes"DOM"— browser APIs needed
Next.js project
{
"compilerOptions": {
"target": "ES2017",
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "ESNext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [{ "name": "next" }],
"paths": { "@/*": ["./src/*"] }
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}
Library (publishing to npm)
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"lib": ["ES2020"],
"strict": true,
"outDir": "./dist",
"rootDir": "./src",
"declaration": true,
"declarationDir": "./dist/types",
"sourceMap": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "node"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "**/*.test.ts"]
}
Key for libraries:
"declaration": true— generates.d.tsfiles for consumers"sourceMap": true— enables debugging in consuming projects
5. extends — inheriting from a base config
Share common settings across multiple projects or configurations:
// tsconfig.base.json
{
"compilerOptions": {
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}
// tsconfig.json (inherits from base)
{
"extends": "./tsconfig.base.json",
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src/**/*"]
}
You can also extend community-maintained configs:
npm install -D @tsconfig/node20
{
"extends": "@tsconfig/node20/tsconfig.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src"
}
}
Available community configs: @tsconfig/node20, @tsconfig/strictest, @tsconfig/recommended.
6. Path aliases
Map import paths to directories:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"],
"@components/*": ["./src/components/*"],
"@utils/*": ["./src/utils/*"]
}
}
}
// Instead of:
import { Button } from "../../../components/Button";
// Use:
import { Button } from "@components/Button";
Note: Path aliases in tsconfig.json only affect TypeScript. Your bundler (Vite, webpack) may need separate alias configuration.
7. Complete options reference table
| Option | Type | Default | Purpose |
|---|---|---|---|
target | string | "ES3" | JS output version |
module | string | depends on target | Module system |
lib | string[] | depends on target | Built-in API types |
strict | boolean | false | All strict checks |
outDir | string | (same as source) | Output directory |
rootDir | string | (inferred) | Source directory |
jsx | string | - | JSX handling mode |
moduleResolution | string | "node" | Import resolution strategy |
esModuleInterop | boolean | false | Default import compatibility |
skipLibCheck | boolean | false | Skip .d.ts checking |
declaration | boolean | false | Generate .d.ts files |
sourceMap | boolean | false | Generate .js.map files |
noEmit | boolean | false | Type-check only, no output |
resolveJsonModule | boolean | false | Allow importing .json files |
isolatedModules | boolean | false | Ensure each file can be compiled independently |
allowJs | boolean | false | Allow .js files in project |
checkJs | boolean | false | Type-check .js files |
incremental | boolean | false | Cache for faster rebuilds |
forceConsistentCasingInFileNames | boolean | false | Enforce case-sensitive imports |
Key takeaways
tsconfig.jsonconfigures the TypeScript compiler — create it withnpx tsc --init.strict: trueenables all strict checks — always recommended for new projects.targetcontrols the JS output version;modulecontrols the module system.- Use
noEmit: truewhen a bundler (Vite, Next.js) handles compilation. include/excludecontrol which files are compiled.- Use
extendsto share configuration across projects or inherit from community configs. - Match your tsconfig to your environment — Node.js, React, Next.js, and libraries each need different settings.
Explain-It Challenge
Explain without notes:
- What does
strict: trueenable, and why is it recommended? - When would you set
noEmit: truein tsconfig? - What is the difference between
targetandmoduleoptions?
Navigation: ← 1.25 Overview · 1.25.k — TypeScript Compiler →