Report #11431
[bug\_fix] Cannot find module './utils.js' or its corresponding type declarations when using ES modules in Node.js
Node.js ESM requires explicit file extensions \(e.g., \`./utils.js\`\) in import specifiers, but TypeScript's classic "node" moduleResolution does not expect or enforce this, leading to a mismatch. To fix, set \`"moduleResolution": "Node16"\` or \`"NodeNext"\` in tsconfig.json. This tells TypeScript to emulate Node.js's native ESM resolution algorithm, which requires extensions and properly handles the \`exports\` field in package.json. You must then write your TypeScript imports with \`.js\` extensions \(even though you are importing \`.ts\` files\), as TypeScript will resolve the \`.js\` to the \`.ts\` source file during compilation.
Journey Context:
A developer migrates their Node.js project to native ES modules by adding \`"type": "module"\` to package.json. Following Node.js requirements, they update all imports to include file extensions: \`import \{ helper \} from './utils.js';\` \(where utils.ts is the source file\). They run \`node --loader ts-node/esm src/index.ts\` and it works at runtime, but TypeScript itself is screaming red errors: "Cannot find module './utils.js' or its corresponding type declarations." The developer is confused—why does the runtime work but the type-checker fail? They try changing the import to \`./utils\` \(no extension\), which satisfies TypeScript but breaks Node.js ESM at runtime with "Cannot find module." They discover that TypeScript's default "node" moduleResolution predates Node's ESM implementation. After reading the TypeScript 4.7 release notes, they change \`"moduleResolution": "NodeNext"\` in tsconfig.json. Immediately, TypeScript recognizes that \`./utils.js\` should resolve to \`./utils.ts\`, and the errors clear.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-16T13:18:39.481134+00:00— report_created — created