Lesson 12 of 13

Modules, Declaration Files & tsconfig

Learn how tsconfig.json controls which files the checks, how strict it is and what JavaScript it outputs. Many confusing build problems come down to these settings.

JSON
{
  "compilerOptions": {
    "target": "ES2022",              // JS version to emit
    "module": "ESNext",              // module syntax to emit
    "moduleResolution": "bundler",   // how imports are resolved (use "node16" for Node)
    "lib": ["ES2022", "DOM"],        // built-in APIs available

    "strict": true,                  // turn this on. All of it. Day one.
    "noUncheckedIndexedAccess": true,// arr[0] is T | undefined — catches real bugs
    "noImplicitOverride": true,
    "exactOptionalPropertyTypes": true,

    "outDir": "dist",
    "rootDir": "src",
    "declaration": true,             // emit .d.ts files for library consumers
    "sourceMap": true,

    "esModuleInterop": true,
    "skipLibCheck": true,            // do not type-check node_modules .d.ts files
    "forceConsistentCasingInFileNames": true,

    "paths": { "@/*": ["./src/*"] }  // import { x } from "@/utils"
  },
  "include": ["src"],
  "exclude": ["node_modules", "dist"]
}
// Import and export forms
export const VERSION = '1.0.0'                 // named export
export function parse(input: string) { /* */ } // named export
export default class Client {}                 // default export (one per file)

export type { User } from './types'            // type-only re-export
export * from './helpers'                      // re-export everything

// Consuming
import Client, { VERSION, parse } from './client'
import type { User } from './types'            // erased at compile time
import { type Config, loadConfig } from './config'  // inline type modifier

// "import type" matters: it guarantees the import disappears from the output,
// so importing a type never pulls a runtime module into your bundle.

// Dynamic import — code splitting, loads only when the branch runs
async function heavyWork() {
  const { default: chart } = await import('./chart-library')
  return chart.render()
}
 
TypeScript (compiler loads on first run)Ctrl+Enter runs
// Declaration files (.d.ts) describe JavaScript that has no types.

// types/legacy-lib.d.ts
declare module 'legacy-lib' {
  export interface Options { retries?: number }
  export function connect(url: string, options?: Options): Promise<void>
  export default function init(): void
}

// Augmenting an existing module's types
declare module 'express' {
  interface Request {
    user?: { id: number; role: 'admin' | 'user' }   // your auth middleware adds this
  }
}

// Global declarations — non-code assets and globals
declare global {
  interface Window { analytics?: { track(event: string): void } }
  namespace NodeJS {
    interface ProcessEnv {
      DATABASE_URL: string
      NODE_ENV: 'development' | 'production' | 'test'
    }
  }
}
declare module '*.svg' {
  const content: string
  export default content
}

export {}   // makes this file a module so "declare global" is legal
 
TypeScript (compiler loads on first run)Ctrl+Enter runs
Finished reading?Mark it done to track your progress.

Tip: use the ← and → keys to move between lessons.