Skip to main content

Module Resolution Overview

Module resolution is the process of determining which file an import statement refers to. TypeScript’s module resolution logic is implemented in src/compiler/moduleNameResolver.ts (181KB).
Module resolution is separate from type checking and happens during the program creation phase.

Resolution Strategies

TypeScript supports multiple module resolution strategies:

ModuleResolutionKind

Classic

Legacy resolution strategy. Rarely used in modern projects.

Node10 (NodeJs)

Mimics Node.js CommonJS resolution. Most common for older projects.

Node16

Supports both ESM and CommonJS with package.json “exports”.

NodeNext

Latest Node.js resolution. Tracks Node.js updates.

Bundler

Optimized for bundlers like webpack, esbuild. Relaxed rules.

Node Resolution Algorithm

Node10 Resolution (Classic Node.js)

For relative imports like import { x } from "./module":
1

Try Extensions

Look for the file with TypeScript extensions:
2

Try Directory Index

If not found, check for directory with index file:
3

Try package.json

Check for package.json with “types” or “typings” field:
For non-relative imports like import { x } from "library":
1

Check node_modules

Look in the current directory’s node_modules:
2

Walk Up Directory Tree

If not found, check parent directories:
3

Resolve Package

Once found, apply relative resolution rules to the package.

Node16/NodeNext Resolution

These modes support package.json exports field:
Node16/NodeNext enforce that ESM imports must include file extensions:

Resolution Features

TypeScript extends Node’s resolution with additional features:

Path Mapping

Configure custom module paths in tsconfig.json:

Base URL

Set a base directory for non-relative imports:

Root Dirs

Treat multiple directories as a single virtual directory:

Module Resolution State

The resolver maintains state during resolution:

Resolution Tracing

Enable detailed resolution logging:
Output:

Resolution Caching

Module resolution results are cached for performance:
The cache is invalidated when:
  • Compiler options change
  • Files are added/removed
  • package.json files change

Resolution Result

Successful resolution returns:

Extension Resolution

The resolver tries different file extensions:

Extension Priority

Resolution order:
  1. module.ts
  2. module.tsx
  3. module.d.ts
  4. module/index.ts
  5. module/index.tsx
  6. module/index.d.ts

Package.json Support

TypeScript respects several package.json fields:

Standard Fields

types

Primary field for TypeScript declarations

typings

Alternative to “types” (older packages)

main

Fallback entry point

Exports Field (Node16+)

Type Versioning

Resolution Modes

Import vs Require Resolution

Triple-Slash Directives

Troubleshooting Resolution

Problem: Cannot find module './utils' or its corresponding type declarations.Solutions:
  • Verify the file exists
  • Check file extension requirements (Node16+)
  • Enable traceResolution to see lookup paths
  • Check baseUrl and paths configuration
Problem: TypeScript resolves to an unexpected file.Solutions:
  • Clear the module resolution cache
  • Check for multiple node_modules directories
  • Verify paths mapping order
  • Review package.json “exports” field
Problem: Library found but no type declarations.Solutions:
  • Install @types/library package
  • Check package.json “types” field
  • Add ambient declarations (.d.ts file)
  • Use skipLibCheck as temporary workaround
Problem: Exports field ignored.Solutions:
  • Use Node16 or NodeNext module resolution
  • Update TypeScript to 4.7+
  • Verify exports field syntax
  • Check for “types” condition in exports

Best Practices

Use Modern Resolution

Prefer Node16 or NodeNext for new projects to support package.json exports.

Avoid Deep Relative Imports

Use path mapping to simplify imports:

Enable Resolution Tracing

Use traceResolution during debugging:

Compiler Overview

Learn about the full compilation pipeline

Emit

Understand JavaScript generation