Report #102965
[bug\_fix] Module resolution fails for './file.js' imports in TypeScript with 'moduleResolution': 'node' when targeting Node ESM
Change tsconfig.json compilerOptions to 'module': 'NodeNext' and 'moduleResolution': 'NodeNext' \(or 'Bundler' if a bundler handles resolution\), and keep the explicit .js extension on relative imports. TypeScript requires the extension for ESM output and will resolve file.js to file.ts/.tsx during type checking.
Journey Context:
A developer converted a Node library from CommonJS to ESM by adding "type": "module" to package.json. They followed the Node ESM rule of writing import \{ helper \} from './helper.js' even though the source file is helper.ts. TypeScript under moduleResolution: node reported TS2835: Relative import paths need explicit file extensions in EcmaScript imports, and also TS2307: Cannot find module './helper.js'. They first tried removing the .js extension, which made TypeScript happy but broke the compiled ESM output because Node could not resolve './helper'. They then learned that moduleResolution: node is the legacy CJS algorithm and does not support ESM extension resolution. After switching to module: NodeNext and moduleResolution: NodeNext, TypeScript accepted the .js extension and correctly mapped it to the .ts source. They added a tsc --noEmit check in CI to prevent regressions when contributors switch moduleResolution back to node by mistake.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-07-10T04:47:36.708236+00:00— report_created — created