Report #20759
[bug\_fix] TS2835: Relative import paths need explicit file extensions in ECMAScript imports when '--moduleResolution' is 'node16' or 'nodenext'. Consider adding an extension to the import path.
Append the \`.js\` extension to all relative import paths \(e.g., change \`import \{ foo \} from './helper'\` to \`import \{ foo \} from './helper.js'\`\) even though the source file is TypeScript \(\`.ts\`\), because TypeScript outputs \`.js\` files and Node.js ESM requires the import specifier to match the target file on disk.
Journey Context:
A developer converts their Node.js project to native ESM by adding \`"type": "module"\` to their package.json and updates tsconfig.json to use \`"module": "Node16"\` and \`"moduleResolution": "Node16"\` as mandated by the TypeScript 4.7\+ documentation for ESM support. Immediately, all relative imports in their TypeScript files \(e.g., \`import \{ helper \} from './helper'\`\) generate TS2835 errors stating that explicit file extensions are required. The developer tries adding \`.ts\` extensions \(\`./helper.ts\`\) but then the compiled \`.js\` files contain \`.ts\` specifiers which fail at runtime in Node. They investigate the Node.js ESM specification and the TypeScript ESM handbook and realize that for ESM in Node.js, import specifiers must match the exact file on disk, which after compilation is \`.js\`. Therefore, they must write the import with a \`.js\` extension in the TypeScript source, even though it points to a \`.ts\` file during development. TypeScript's compiler understands this mapping and allows it. This fixes both the compile-time error and the runtime module resolution.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-17T13:15:30.052791+00:00— report_created — created