tsconfig Deep Dive

Difficulty: Beginner

Question

What are the most important tsconfig.json options? Explain strict mode and how compiler settings affect your code.

Answer

tsconfig.json configures the TypeScript compiler. The most impactful settings are:

strict: true - enables all strict type checking options: - strictNullChecks: null/undefined are distinct types, not assignable to other types - strictFunctionTypes: stricter function parameter checking - strictPropertyInitialization: class properties must be initialized - noImplicitAny: error when TypeScript cannot infer a type - noImplicitThis: error when 'this' has an implicit 'any' type

target: which JS version to compile to (ES5, ES2020, ESNext) module: module system (CommonJS, ESNext, NodeNext) moduleResolution: how imports are resolved (node, bundler, nodenext) paths: alias mappings for imports (@/ to src/)

Always use strict: true for new projects. It catches the most bugs.

Code examples

Essential tsconfig Settings

// tsconfig.json for a modern project
{
  "compilerOptions": {
    // Type Checking
    "strict": true,                    // Enable ALL strict checks
    "noUncheckedIndexedAccess": true,  // obj[key] returns T | undefined
    "noUnusedLocals": true,            // Error on unused variables
    "noUnusedParameters": true,        // Error on unused params
    "exactOptionalPropertyTypes": true, // undefined !== missing

    // Compilation
    "target": "ES2022",               // Output JS version
    "module": "ESNext",               // Module system
    "moduleResolution": "bundler",    // For Vite/webpack projects
    "lib": ["ES2022", "DOM", "DOM.Iterable"],

    // Output
    "outDir": "./dist",
    "declaration": true,              // Generate .d.ts files
    "sourceMap": true,                // Generate source maps
    "skipLibCheck": true,             // Skip checking .d.ts files

    // Path aliases
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"],
      "@components/*": ["./src/components/*"],
      "@utils/*": ["./src/utils/*"]
    }
  },
  "include": ["src//*"],
  "exclude": ["node_modules", "dist"]
}

This configuration enables maximum type safety with strict: true and noUncheckedIndexedAccess. Path aliases make imports cleaner. The bundler module resolution works with modern build tools.

What strict: true Enables

// strictNullChecks: true
function greet(name: string | null) {
  // return name.toUpperCase(); // Error: 'name' is possibly 'null'
  return name?.toUpperCase() ?? 'GUEST';
}

// noImplicitAny
// function add(a, b) { // Error: Parameter 'a' implicitly has an 'any' type
function add(a: number, b: number) { return a + b; } // OK

// strictPropertyInitialization
class User {
  name: string;
  // email: string; // Error: not assigned in constructor
  email!: string;   // OK with ! (definite assignment)

  constructor(name: string) {
    this.name = name;
  }
}

// strictFunctionTypes
type Handler = (event: MouseEvent) => void;
// const handler: Handler = (event: Event) => {}; // Error: Event not assignable to MouseEvent

// noImplicitThis
class Timer {
  seconds = 0;
  start() {
    setInterval(function () {
      // this.seconds++; // Error: 'this' implicitly has type 'any'
    }, 1000);
    setInterval(() => {
      this.seconds++; // OK: arrow function captures 'this'
    }, 1000);
  }
}

Each strict flag catches a different category of bugs. strictNullChecks alone prevents the 'billion dollar mistake' of null reference errors.

Module Resolution & Target

// target affects which JS features are transpiled
// target: ES5 -> compiles async/await to generators
// target: ES2017 -> keeps async/await native
// target: ES2022 -> keeps class fields, top-level await

// module affects import/export output
// "module": "CommonJS" -> const express = require('express');
// "module": "ESNext"   -> import express from 'express';

// moduleResolution strategies
// "bundler" - for Vite, webpack, esbuild (most modern projects)
//   - supports package.json exports field
//   - allows extensionless imports
// "nodenext" - for Node.js projects
//   - requires file extensions in imports
//   - respects package.json exports/imports
// "node" - legacy Node.js resolution (avoid for new projects)

// Path aliases in tsconfig
// {
//   "paths": { "@/*": ["./src/*"] }
// }
// Note: tsconfig paths only affect TYPE resolution.
// Your bundler (Vite) or Node.js (tsx) also needs
// matching alias configuration.

// Example Vite config to match:
// resolve: {
//   alias: { '@': path.resolve(__dirname, 'src') }
// }

import { UserService } from '@/services/user'; // uses @/ alias
import { formatDate } from '@utils/date';       // uses @utils/ alias

target controls JS output version. moduleResolution controls how imports are resolved. Path aliases need matching config in both tsconfig and your build tool.

Key points

Concepts covered

tsconfig.json, Compiler Options, strict Mode, Module Resolution, Target, Paths