tezvyn:

TypeScript's Module Resolution Strategy

AI-drafted, machine-checkedSource: typescriptlang.orgintermediate

TypeScript's `moduleResolution` is the compiler's search plan for finding files behind `import` statements. It's crucial when mixing module types (ESM/CJS) or targeting Node.js vs. the browser. The footgun is assuming the default `node` works for modern ESM.

WHY IT EXISTS: JavaScript code is split into files, or modules. When you write import { MyClass } from './utils', the TypeScript compiler needs to find the ./utils file to read its type information. Different environments like Node.js and web browsers have different rules for finding that file. The moduleResolution setting exists to tell TypeScript which set of rules to follow so its compile-time checks match the eventual runtime behavior.

THE MENTAL MODEL: Think of moduleResolution as a detective's search plan. Given a clue (the import path, like 'lodash'), the strategy dictates the exact steps to find the target (the actual file on disk). Should it look for lodash.ts, lodash/index.ts, or check a package.json file for directions? The strategy defines this process, aiming to perfectly mirror how the runtime would find it.

HOW IT WORKS: TypeScript offers several strategies in tsconfig.json. The two most important are node and nodenext. The node strategy mimics the classic Node.js CommonJS (require()) algorithm: it checks for relative files, then traverses up the directory tree looking in node_modules folders. The nodenext strategy is for modern projects; it understands ECMAScript Modules (ESM) and new features like the exports field in package.json, which provides a map to a package's entry points.

WHEN TO USE IT: You always have a module resolution strategy, but you should explicitly set it to match your project's target. Use "moduleResolution": "node" for traditional CommonJS projects or web projects using a bundler that follows Node's logic. Use "nodenext" or "bundler" for modern Node.js projects using ESM or for bundlers that respect the exports field.

WHEN NOT TO USE IT: Avoid using a strategy that mismatches your runtime. For example, do not use the default node strategy if your Node.js project uses ESM ("type": "module" in package.json) and depends on packages that use the exports field. TypeScript won't find the correct module files, leading to compile-time "Cannot find module" errors that predict a crash at runtime.

ONE CANONICAL EXAMPLE: Your code has import { a } from 'my-lib'. With "moduleResolution": "node", TypeScript looks for node_modules/my-lib/index.ts (and other variations). With "moduleResolution": "nodenext", it first checks node_modules/my-lib/package.json. If that file has an "exports" field pointing to "./dist/index.mjs", TypeScript knows to look there for the types, correctly modeling how a modern runtime would load the package.

Read the original → typescriptlang.org

Get five bites like this every day.

Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.