Jesse A. Green / notebook

TypeScript User Guide

Generated May 2026 from microsoft/TypeScript at f350b52

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

1. Quick Start

TypeScript is a strongly-typed programming language that builds on JavaScript by adding static type definitions, enabling developers to catch errors at compile time rather than at runtime. Created by Microsoft, TypeScript compiles to plain JavaScript and runs anywhere JavaScript runs — browsers, Node.js, Deno, and more.

Key features:

  • Static type checking that catches bugs before code ships
  • Full JavaScript interoperability — every valid JS file is valid TS
  • Rich IDE support with IntelliSense, refactoring, and navigation
  • Modern ECMAScript feature support with downlevel compilation
  • Declaration files (.d.ts) for typing JavaScript libraries
  • Project references for monorepo and multi-project builds
  • Watch mode for instant recompilation on file changes
  • Incremental compilation for fast rebuilds

Prerequisites: Node.js 14.17 or later

Install and compile your first file:

npm install -g typescript

Create a file hello.ts:

function greet(name: string): string {
  return `Hello, ${name}!`;
}

console.log(greet("World"));

Compile and run:

tsc hello.ts
node hello.js
Hello, World!

Tip

Skip the global install — most projects use a local typescript devDependency and run npx tsc instead. This pins your project to a specific TypeScript version.

What to try next: Initialize a project configuration with tsc --init, explore strict mode, or set up watch mode with tsc --watch.


2. Installation

System Requirements

Requirement Minimum Recommended
Node.js 14.17 22.x (LTS)
npm 6.x 8.x+
Disk space ~50 MB ~50 MB
OS Windows, macOS, Linux Any

Installation Methods

npm (recommended)

npm install --save-dev typescript

yarn

yarn add --dev typescript

pnpm

pnpm add -D typescript

Global install

npm install -g typescript

Important

Install TypeScript as a devDependency in your project rather than globally. This ensures all team members and CI pipelines use the same version.

Post-Install Verification

npx tsc --version
Version 6.0.0

Verify the language server is available:

npx tsserver --version

Install Troubleshooting

Error: tsc: command not found

Cause: TypeScript is installed locally but not on PATH.

Solution:

  1. Use npx tsc instead of tsc
  2. Or add an npm script: "build": "tsc" in package.json
  3. Or install globally: npm install -g typescript

Prevention: Always use npx tsc for local installations.

Error: Cannot find module 'typescript'

Cause: TypeScript is not installed in the current project.

Solution:

  1. Run npm install --save-dev typescript
  2. Verify with npx tsc --version

Prevention: Include typescript in devDependencies.

Error: npm ERR! code EACCES

Cause: Insufficient permissions for global install.

Solution:

  1. Use sudo npm install -g typescript (Linux/macOS)
  2. Or configure npm to use a user-level global directory
  3. Or use npx tsc without global install

Prevention: Use local installs or configure npm prefix.

Error: engine "node" is incompatible

Cause: Node.js version is below the minimum requirement of 14.17.

Solution:

  1. Update Node.js: nvm install --lts or download from nodejs.org
  2. Verify: node --version

Prevention: Use a Node.js version manager (nvm, fnm, Volta).


3. Configuration

TypeScript projects are configured through tsconfig.json, a JSON file at the project root that specifies compiler options, file inclusion/exclusion patterns, and project references.

Initialize a Configuration

npx tsc --init

This generates a tsconfig.json with recommended defaults:

{
  "compilerOptions": {
    "module": "nodenext",
    "target": "esnext",
    "strict": true,
    "jsx": "react-jsx",
    "verbatimModuleSyntax": true,
    "isolatedModules": true,
    "noUncheckedSideEffectImports": true,
    "moduleDetection": "force",
    "skipLibCheck": true,
    "sourceMap": true,
    "declaration": true,
    "declarationMap": true,
    "noUncheckedIndexedAccess": true,
    "exactOptionalPropertyTypes": true,
    "types": []
  }
}

Common Option Combinations

Scenario Key Options
Node.js app "module": "nodenext", "target": "esnext", "types": ["node"]
React frontend "module": "esnext", "target": "es2020", "jsx": "react-jsx"
Library "declaration": true, "declarationMap": true, "outDir": "./dist"
Strict checking "strict": true, "noUncheckedIndexedAccess": true, "exactOptionalPropertyTypes": true
Monorepo "composite": true, "references": [...], "declarationMap": true

Essential Compiler Options

Option Default Description
target es5 ECMAScript version for output
module varies Module system (nodenext, esnext, commonjs)
strict false Enable all strict type-checking options
outDir (same as source) Output directory for compiled files
rootDir (inferred) Root directory of source files
declaration false Generate .d.ts declaration files
sourceMap false Generate .js.map source map files
jsx (none) JSX support (react, react-jsx, preserve)
lib (target-dependent) Library files to include in compilation
skipLibCheck false Skip type checking of declaration files

Tip

Start with "strict": true for new projects. It enables strictNullChecks, strictFunctionTypes, strictBindCallApply, strictPropertyInitialization, noImplicitAny, noImplicitThis, alwaysStrict, and useUnknownInCatchVariables all at once.

File Inclusion

{
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist", "**/*.spec.ts"]
}
Property Purpose
include Glob patterns for files to compile
exclude Glob patterns to skip (defaults to node_modules, bower_components, jspm_packages, outDir)
files Explicit file list (no globs)

Note

For the full configuration reference with every compiler option, see the Admin Guide § Configuration Reference.


4. Core Usage

Compiling TypeScript

Compile a project (uses tsconfig.json):

npx tsc

Compile specific files:

npx tsc file1.ts file2.ts
# Output: file1.js, file2.js in the same directory

Compile with inline options:

npx tsc --target es2020 --module nodenext --outDir dist src/index.ts

Warning

When you pass files directly to tsc, the tsconfig.json file is ignored. Use tsc -p tsconfig.json or just tsc (without file arguments) to use your config.

Type-check only (no output):

npx tsc --noEmit

This is useful in CI pipelines where a separate bundler (esbuild, Vite, webpack) handles the actual compilation.

Show the resolved configuration:

npx tsc --showConfig

Watch Mode

Watch mode recompiles automatically when files change:

npx tsc --watch
[12:00:00 AM] Starting compilation in watch mode...
[12:00:01 AM] Found 0 errors. Watching for file changes.

Tip

Combine --watch with --preserveWatchOutput to prevent the terminal from being cleared on each recompilation.

Watch Mode Environment Variables

Variable Purpose
TSC_WATCHFILE Override the file watching strategy
TSC_WATCHDIRECTORY Override the directory watching strategy
TSC_NONPOLLING_WATCHER Use a non-polling watcher when available

Incremental Compilation

Enable incremental builds for faster recompilation:

{
  "compilerOptions": {
    "incremental": true,
    "tsBuildInfoFile": "./.tsbuildinfo"
  }
}

TypeScript saves a .tsbuildinfo file that records the state of the last compilation. Subsequent builds only recompile changed files.

Project References

For monorepos or multi-package projects, use project references to compile interdependent projects efficiently:

{
  "compilerOptions": {
    "composite": true,
    "declaration": true,
    "declarationMap": true
  },
  "references": [
    { "path": "../shared" },
    { "path": "../utils" }
  ]
}

Build all referenced projects:

npx tsc --build
# Builds projects in dependency order
Build Flag Purpose
--build (-b) Build project references in dependency order
--clean Delete outputs of all referenced projects
--dry Show what would be built without building
--force Force rebuild even if outputs are up to date
--verbose Show detailed build information

Note

Projects using --build mode must set "composite": true and "declaration": true in their tsconfig.json.

Declaration Files

Generate type declaration files (.d.ts) for libraries:

npx tsc --declaration --emitDeclarationOnly --outDir types

For isolated declaration emit (faster, per-file):

{
  "compilerOptions": {
    "declaration": true,
    "isolatedDeclarations": true
  }
}

Source Maps

Enable source maps to debug TypeScript directly:

{
  "compilerOptions": {
    "sourceMap": true,
    "declarationMap": true
  }
}
Option Output Use Case
sourceMap .js.map Debug JS back to TS source
inlineSourceMap Embedded in .js Single-file debug output
declarationMap .d.ts.map Navigate from declaration to source
sourceRoot (path) Override source root in source maps
mapRoot (path) Override map file location

5. Common Tasks

Task 1: Migrate a JavaScript Project to TypeScript

Goal: Gradually adopt TypeScript in an existing JavaScript codebase.

Steps:

  1. Install TypeScript:

    npm install --save-dev typescript @types/node
    
  2. Create tsconfig.json with permissive settings:

    {
      "compilerOptions": {
        "allowJs": true,
        "checkJs": false,
        "target": "esnext",
        "module": "nodenext",
        "outDir": "./dist",
        "strict": false
      },
      "include": ["src"]
    }
    
  3. Rename files from .js to .ts one at a time

  4. Fix type errors as they appear

  5. Gradually enable strict options:

    "noImplicitAny": true,
    "strictNullChecks": true
    

Verify: npx tsc --noEmit reports zero errors.

Tip

Use // @ts-ignore or // @ts-expect-error as temporary escape hatches during migration. Remove them once proper types are added.

Task 2: Set Up TypeScript with a Bundler

Goal: Use TypeScript with esbuild, Vite, or webpack for frontend development.

Steps:

  1. Configure TypeScript for type-checking only:

    {
      "compilerOptions": {
        "noEmit": true,
        "isolatedModules": true,
        "verbatimModuleSyntax": true,
        "moduleDetection": "force",
        "module": "esnext",
        "target": "esnext",
        "strict": true
      }
    }
    
  2. Add scripts to package.json:

    {
      "scripts": {
        "typecheck": "tsc --noEmit",
        "typecheck:watch": "tsc --noEmit --watch"
      }
    }
    
  3. Let the bundler handle compilation; TypeScript handles type checking

Verify: npm run typecheck passes with no errors.

Important

When using isolatedModules, every file must be independently parseable. This means no const enum across files, no namespace merging, and every file must have at least one import or export.

Task 3: Type-Check JavaScript Files with JSDoc

Goal: Get TypeScript's type checking benefits without renaming files.

Steps:

  1. Enable JS checking in tsconfig.json:

    {
      "compilerOptions": {
        "allowJs": true,
        "checkJs": true,
        "noEmit": true
      },
      "include": ["src/**/*.js"]
    }
    
  2. Add JSDoc type annotations to your JavaScript:

    /**
     * @param {string} name
     * @returns {string}
     */
    function greet(name) {
      return `Hello, ${name}!`;
    }
    
  3. Run the type checker: npx tsc --noEmit

Verify: Type errors are reported for JSDoc-annotated code.

Task 4: Configure Path Aliases

Goal: Use import aliases like @app/utils instead of relative paths.

Steps:

  1. Add paths to tsconfig.json:

    {
      "compilerOptions": {
        "baseUrl": ".",
        "paths": {
          "@app/*": ["src/*"],
          "@lib/*": ["lib/*"]
        }
      }
    }
    
  2. Use in code:

    import { helper } from "@app/utils";
    

Warning

tsc does NOT rewrite path aliases in output. Your bundler or a runtime plugin (like tsconfig-paths) must resolve them. There is active discussion about deprecating baseUrl (see community issue #62207).

Task 5: Generate API Documentation Types

Goal: Export declaration files for a library.

Steps:

  1. Configure for library output:

    {
      "compilerOptions": {
        "declaration": true,
        "declarationMap": true,
        "emitDeclarationOnly": true,
        "outDir": "./dist"
      },
      "include": ["src"]
    }
    
  2. Set package.json entry points:

    {
      "main": "./dist/index.js",
      "types": "./dist/index.d.ts"
    }
    
  3. Build declarations: npx tsc

Verify: .d.ts files exist in ./dist and IDE navigation works from consuming projects.

Task 6: Set Up Monorepo with Project References

Goal: Compile interdependent packages efficiently.

Steps:

  1. Create a root tsconfig.json:

    {
      "files": [],
      "references": [
        { "path": "packages/shared" },
        { "path": "packages/app" }
      ]
    }
    
  2. Each package sets "composite": true:

    {
      "compilerOptions": {
        "composite": true,
        "declaration": true,
        "outDir": "./dist",
        "rootDir": "./src"
      },
      "references": [{ "path": "../shared" }]
    }
    
  3. Build all: npx tsc --build

  4. Clean all: npx tsc --build --clean

Verify: npx tsc --build --dry shows the correct build order.

Task 7: Use TypeScript with React (JSX)

Goal: Configure TypeScript for React development.

Steps:

  1. Install dependencies:

    npm install --save-dev typescript @types/react @types/react-dom
    
  2. Configure tsconfig.json:

    {
      "compilerOptions": {
        "jsx": "react-jsx",
        "module": "esnext",
        "target": "es2020",
        "moduleResolution": "bundler",
        "strict": true
      }
    }
    
  3. Write components with .tsx extensions:

    interface Props {
      name: string;
    }
    
    export function Greeting({ name }: Props) {
      return <h1>Hello, {name}!</h1>;
    }
    

Verify: npx tsc --noEmit passes.

JSX Option Output Use Case
react-jsx _jsx() calls (React 17+) Modern React apps
react React.createElement() Legacy React
preserve Keep JSX as-is Bundler handles transform
react-native Keep JSX as-is React Native

Task 8: Enable Strict Null Checks Incrementally

Goal: Adopt strictNullChecks in a large existing codebase.

Steps:

  1. Enable the flag:

    { "compilerOptions": { "strictNullChecks": true } }
    
  2. Fix errors file by file — common patterns:

    // Before: error TS2532 — Object is possibly 'undefined'
    const len = arr.length;
    
    // After: null guard
    const len = arr?.length ?? 0;
    
  3. Use // @ts-expect-error for known issues you'll fix later

Verify: Error count decreases with each file fixed.

Task 9: Use TypeScript with ESLint

Goal: Lint TypeScript code with type-aware rules.

Steps:

  1. Install:

    npm install --save-dev eslint typescript-eslint
    
  2. Configure eslint.config.mjs:

    import tseslint from "typescript-eslint";
    
    export default tseslint.config(
      ...tseslint.configs.recommended,
      {
        languageOptions: {
          parserOptions: {
            projectService: true,
          },
        },
      }
    );
    
  3. Run: npx eslint .

Verify: ESLint reports type-aware rule violations.

Task 10: Format TypeScript Code with dprint

Goal: Use the same formatter the TypeScript project itself uses.

Steps:

  1. Install: npm install --save-dev dprint @dprint/typescript
  2. Create dprint.json:
    {
      "typescript": {},
      "includes": ["**/*.{ts,tsx}"]
    }
    
  3. Format: npx dprint fmt

Verify: npx dprint check passes with no differences.


6. Troubleshooting

Diagnostic Decision Tree

Error during compilation?
├── Type error (TS2xxx) → Check types, interfaces, annotations
├── Module not found (TS2307) → Check moduleResolution, paths, installed @types
├── Config error (TS5xxx) → Check tsconfig.json syntax and option compatibility
├── Syntax error (TS1xxx) → Check for unsupported syntax or wrong target
└── Declaration error (TS4xxx) → Check export visibility and declaration settings

Error: TS2307: Cannot find module './module' or its corresponding type declarations

Cause: TypeScript cannot resolve the import. Common reasons: missing file, wrong moduleResolution, missing @types package, or incorrect paths configuration.

Solution:

  1. Verify the file exists at the referenced path
  2. Check moduleResolution matches your runtime (nodenext for Node.js, bundler for bundlers)
  3. For third-party packages: npm install --save-dev @types/package-name
  4. For path aliases: verify baseUrl and paths in tsconfig.json

Prevention: Use "moduleResolution": "nodenext" for Node.js projects and "moduleResolution": "bundler" for bundler-based projects.

Error: TS2322: Type 'X' is not assignable to type 'Y'

Cause: A value's type doesn't match the expected type at that position.

Solution:

  1. Check if a type assertion is appropriate: value as Type
  2. Narrow the type with a type guard: if (typeof value === 'string')
  3. Update the type annotation to match the actual value
  4. Use union types if both types are valid: Type1 | Type2

Prevention: Enable strict mode to catch type mismatches early.

Error: TS18003: No inputs were found in config file

Cause: No .ts files match the include/files patterns in tsconfig.json.

Solution:

  1. Add at least one .ts file to your source directory
  2. Check that include patterns match your file structure
  3. Verify exclude isn't filtering out all files

Prevention: Run tsc --showConfig to verify file inclusion patterns.

Error: TS5023: Unknown compiler option

Cause: A tsconfig.json option is misspelled or not supported in the installed TypeScript version.

Solution:

  1. Check the option name against the documentation
  2. Verify your TypeScript version supports the option: npx tsc --version
  3. Update TypeScript if the option was added in a newer version

Prevention: Use an IDE with TypeScript config IntelliSense for autocomplete in tsconfig.json.

Error: TS6059: File is not under 'rootDir'

Cause: A source file is outside the rootDir boundary, which breaks the output directory structure.

Solution:

  1. Move the file under rootDir
  2. Or expand rootDir to include the file
  3. Or add the file's directory to include and adjust rootDir

Prevention: Set rootDir explicitly to the common ancestor of all source files.

Error: TS1259: Module can only be default-imported using the 'esModuleInterop' flag

Cause: Importing a CommonJS module with import x from 'module' syntax without interop.

Solution:

  1. Enable "esModuleInterop": true in tsconfig.json
  2. Or use namespace import: import * as x from 'module'

Prevention: Use "verbatimModuleSyntax": true for new projects (replaces esModuleInterop).

Error: TS2688: Cannot find type definition file for 'node'

Cause: Using Node.js APIs without the Node type definitions.

Solution:

  1. Install: npm install --save-dev @types/node
  2. Add to tsconfig.json: "types": ["node"]

Prevention: Always install @types/node for Node.js projects.

Error: VSCode TypeScript Language Server Keeps Crashing

Cause: Large projects or circular dependencies can exhaust the language server's memory. Community reports this in issue #63405 and #63151.

Solution:

  1. Restart the TS server: Ctrl+Shift+P → "TypeScript: Restart TS Server"
  2. Increase Node.js memory: set "typescript.tsserver.maxTsServerMemory" in VSCode settings
  3. Exclude large generated or vendor directories from the project
  4. Check for circular tsconfig.json references

Prevention: Use skipLibCheck: true and exclude test/generated files from the main project.

Warning

If the language server crashes repeatedly, check for overly complex conditional types or deeply nested generics. These can cause exponential type-checking time.

Error: TS2345: Argument of type 'X' is not assignable to parameter of type 'Y' with Generics

Cause: Generic type inference produces a narrower or different type than expected.

Solution:

  1. Explicitly provide type arguments: fn<ExpectedType>(arg)
  2. Use satisfies to check types without widening: const x = value satisfies Type
  3. Check if the generic constraint is too narrow

Prevention: Test generic functions with multiple type argument combinations.

Caution

Avoid using any to silence type errors. Instead, use unknown and narrow the type, or use type assertions with clear justification.


7. FAQ

Q: What is the difference between interface and type?

Both define the shape of data. interface supports declaration merging (multiple declarations with the same name are combined) and extends for inheritance. type supports unions (A | B), intersections (A & B), mapped types, and conditional types. Use interface for object shapes and public APIs; use type for unions, utility types, and complex compositions.

Q: Should I use any or unknown?

Use unknown. It requires type narrowing before use, which catches bugs. any disables all type checking for that value and propagates silently. Use any only as a last resort during migration.

Q: What does strict: true actually enable?

It enables these individual flags: strictNullChecks, strictFunctionTypes, strictBindCallApply, strictPropertyInitialization, noImplicitAny, noImplicitThis, alwaysStrict, and useUnknownInCatchVariables. New flags may be added under strict in future versions.

Q: How do I check types without emitting output?

Use npx tsc --noEmit. This runs the full type checker without generating any JavaScript output. Useful in CI and when a bundler handles compilation.

Q: What is isolatedModules and should I enable it?

isolatedModules ensures each file can be independently transpiled (without cross-file information). Enable it when using a single-file transpiler like esbuild, SWC, or Babel. It disables features that require whole-program analysis, such as const enum across files and namespace merging.

Q: How do I use TypeScript with CommonJS and ESM simultaneously?

Set "module": "nodenext" and use .cts/.mts extensions to explicitly mark files. Files with .ts extension follow package.json's "type" field. This is the recommended approach for Node.js projects.

Q: What is verbatimModuleSyntax?

It enforces that import/export syntax in the source matches what appears in the output. import type { X } is erased; import { X } is preserved. This replaces the older importsNotUsedAsValues and preserveValueImports options and makes module behavior predictable.

Q: How do I handle @types packages?

TypeScript automatically discovers @types packages from node_modules/@types/. Control which are included with the types array in tsconfig.json. Set "types": [] to include none, or "types": ["node", "jest"] to include specific ones.

Q: What is the difference between moduleResolution: "node16", "nodenext", and "bundler"?

node16 and nodenext follow Node.js's native ESM resolution rules (package.json exports, .js extension in imports). nodenext always tracks the latest Node.js behavior. bundler follows bundler conventions (no extension required, supports exports but is more lenient). Use nodenext for Node.js; use bundler for Vite/webpack/esbuild projects.

Q: What is the satisfies operator?

satisfies checks that a value conforms to a type without widening the inferred type:

const palette = {
  red: [255, 0, 0],
  green: "#00ff00",
} satisfies Record<string, string | number[]>;
// palette.red is still number[], not string | number[]

Q: What changed in TypeScript 6.0?

TypeScript 6.0 is the current major release. Key areas of focus include module system improvements, default module setting changes (see issue #63330 for community discussion on default behavior), and the --rewriteRelativeImportExtensions flag for rewriting .ts extensions in output. Check the TypeScript 6.0 Iteration Plan (issue #63085) for the full scope.

Note

Community issue #63392 discusses updating the official TSConfig documentation at typescriptlang.org for TypeScript 6.0 changes. Some documented defaults may lag behind the actual compiler behavior.

Q: How do I speed up type checking in large projects?

  • Enable "skipLibCheck": true to skip checking declaration files
  • Use project references to split large projects into smaller units
  • Enable "incremental": true for faster subsequent builds
  • Exclude tests and generated code from the main compilation
  • Use --noEmit with a fast bundler for development