Report #92984
[bug\_fix] Relative import paths need explicit file extensions in ECMAScript imports when '--moduleResolution' is 'node16' or 'nodenext'. Consider adding '.js' to the import path.
Add the \`.js\` extension to relative imports in TypeScript source files \(e.g., \`import \{ foo \} from './utils/foo.js'\`\). Even though the source file is \`.ts\`, TypeScript emits \`.js\` and ES modules require the extension in the specifier. Do not use \`.ts\` extensions in the import.
Journey Context:
Developer migrates a project to ES modules \(\`"type": "module"\` in package.json\) and sets TypeScript's \`moduleResolution\` to \`"Node16"\` \(or \`"NodeNext"\`\) as required for ESM support. Immediately, all relative imports like \`import \{ helper \} from './utils/helper'\` error with "Relative import paths need explicit file extensions...". The developer tries changing it to \`./utils/helper.ts\`, but TypeScript errors that it cannot find the module or that the extension is not recognized. Confused, they check the emitted JavaScript and see that \`helper.ts\` becomes \`helper.js\`. They realize that under ES modules \(ESM\) specifications, import specifiers must be valid URLs, including the file extension. Since TypeScript emits \`.js\` files, the import in the source TypeScript must reference the emitted file name: \`./utils/helper.js\`. This feels counter-intuitive because the source file is \`.ts\`, but it aligns the source with the output for runtime Node.js ESM resolution. The developer updates all imports to use \`.js\` extensions, and \`tsc\` now compiles without errors, and the application runs correctly in Node.js ESM mode.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-22T14:39:50.846529+00:00— report_created — created