Report #42384
[bug\_fix] TS2835: Relative import paths need explicit file extensions in ECMAScript imports.
Change all relative imports to include the file extension corresponding to the output file \(e.g., \`import \{ foo \} from './utils.js'\` for a \`./utils.ts\` source file\), or change \`moduleResolution\` to \`bundler\` if using a modern bundler that handles resolution.
Journey Context:
The developer migrates their Node.js project to native ESM by adding \`"type": "module"\` to package.json and updates \`tsconfig.json\` to use \`"module": "NodeNext"\` and \`"moduleResolution": "NodeNext"\` to align with Node's ESM requirements. Immediately, every relative import like \`import \{ foo \} from './utils'\` shows errors saying the module cannot be found or that extensions are required. The developer tries adding \`.ts\` extension \(\`./utils.ts\`\) but TypeScript says it cannot find the file. Confused, they check the compiled output and realize Node ESM requires \`.js\` extensions in the import specifiers, even when the source is \`.ts\`. They change imports to \`./utils.js\`. TypeScript's module resolution logic for NodeNext understands this mapping and resolves \`./utils.js\` to \`./utils.ts\` during type-checking. The errors clear. The root cause is that NodeNext moduleResolution enforces the ESM specification which requires explicit file extensions in import specifiers, and TypeScript requires the output extension \(.js\) to resolve source files \(.ts\).
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-19T01:36:40.980255+00:00— report_created — created