TypeScript Module Resolution
Module resolution is the set of rules the compiler follows to find the file behind an import. The moduleResolution setting picks the rule set, and node16 follows the strict rules of modern Node.
A tsconfig with node16 resolution
The config sets both module and moduleResolution to node16 and turns on strict checks. The closing example of this lesson is compiled by this file as a small project.
{
"compilerOptions": {
"target": "es2020",
"module": "node16",
"moduleResolution": "node16",
"strict": true,
"outDir": "out"
}
}The util.ts file
The helper file exports one greet function. Every import in this lesson resolves to this file.
export function greet(userName: string): string {
return 'Hello, ' + userName + '!';
}An extensionless import under node mode
The default node lookup tries the path as a file and adds the extension while searching. This program uses the default mode and runs.
import { greet } from './util';
console.log(greet('Ada'));node16 refuses a missing extension
A dynamic import is an ECMAScript import, so node16 applies the extension rule to its specifier. The specifier names no extension and the real compiler refusal follows.
async function main(): Promise<void> {
const mod = await import('./util');
console.log(mod.greet('Ada'));
}
void main();Example
import { greet } from './util.js';
console.log(greet('Ada'));
console.log(greet('Grace'));Key points
- Classic resolution walks up the folder tree looking for a matching file name.
- Node resolution mirrors older CommonJS lookup, so it tries the file itself, then a folder index.
- node16 resolution follows modern Node, where ECMAScript imports need an explicit file extension.
- The written import names the emitted file, so a TypeScript source imported under node16 is named with its .js extension.
- Bundler resolution suits projects built by a bundler and is named here for completeness.
In this example, the project compiles under its own node16 tsconfig, the import names util.js with its extension, and Node runs the emitted files.
Note
The setting changes how imports are found, never what the code means once found.
Try it Yourself
Edit the code, then press Run.
Running is simulated ยท TypeScript Playground
Exercise
Under node16, how does an ECMAScript import name a sibling TypeScript file?