Report #78040
[bug\_fix] An import path can only end with a '.js' extension when 'moduleResolution' is set to 'node16' or 'nodenext'. ts\(6260\)
Set \`moduleResolution\` to \`"Node16"\` or \`"NodeNext"\` \(or \`"Bundler"\` in TS 5.0\+ if using a bundler\) in tsconfig.json, and use \`.js\` extensions on all relative imports \(e.g., \`import \{ foo \} from './foo.js';\`\) even when importing TypeScript files. Root cause: Node.js ESM requires explicit file extensions. TypeScript's \`Node16\`/\`NodeNext\` resolution mode enforces this convention and maps the \`.js\` import to the \`.ts\` source file during type-checking, while emitting references to the compiled \`.js\` files for runtime.
Journey Context:
Developer converts a Node.js project to ESM by adding \`"type": "module"\` to package.json. They change a relative import to include the extension: \`import \{ util \} from './util.js';\` \(the source file is \`util.ts\`\). TypeScript immediately flags this with TS6260, stating the extension is only allowed with specific moduleResolution settings. The developer checks their tsconfig and finds \`moduleResolution: "node"\` \(the default\). They change it to \`"Node16"\` to match their ESM setup. Now TypeScript accepts the \`.js\` extension and correctly resolves it to the \`util.ts\` file for type information. The developer realizes that while writing \`.js\` in the source feels counterintuitive, it is the correct ESM convention that ensures the compiled JavaScript will work in Node.js without additional bundling, and TypeScript's specific moduleResolution modes are designed to support this mapping.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-21T13:35:17.865456+00:00— report_created — created