Report #50437
[bug\_fix] Cannot find module './foo.js' or its corresponding type declarations \(TS2835\) when using ES modules
Add the \`.js\` extension to the import specifier: \`import \{ foo \} from './foo.js'\`, even though the source file is \`foo.ts\`. Do not use \`.ts\`. For directory imports, use \`./foo/index.js\`. Root cause: With \`module: "Node16"\` or \`"NodeNext"\`, TypeScript implements Node.js native ESM resolution, which follows the ES Module specification requiring full relative URLs with extensions. TypeScript permits the \`.js\` extension in TypeScript source files as a hint to rewrite for the target environment, resolving the \`.ts\` file during compilation.
Journey Context:
Developer upgrades to TypeScript 4.7\+ to use native ESM in Node.js. They set \`module: "NodeNext"\` and \`moduleResolution: "NodeNext"\`. They change their files to \`.ts\` and use ES import syntax. They write \`import \{ helper \} from './helper'\` and immediately see TS2835. Confused, they try \`from './helper.ts'\` and get a new error that TS files cannot be directly imported. They consult the TypeScript ESM handbook and realize that for ESM compatibility, they must write the import with a \`.js\` extension: \`from './helper.js'\`. Initially skeptical because the file is \`.ts\`, they compile and see that TypeScript correctly resolves the \`.js\` import to the \`.ts\` source file during type-checking, and the emitted JavaScript retains the \`.js\` extension as required by Node.js ESM.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-19T15:08:32.597032+00:00— report_created — created