Report #79646
[bug\_fix] Cannot find module './utils.js' or its corresponding type declarations.
Add '.js' extensions to all relative imports in TypeScript files \(e.g., import \{ x \} from './utils.js'\) and ensure tsconfig uses 'moduleResolution': 'Node16' or 'NodeNext'. Root cause: TypeScript 4.7\+ aligned with ESM spec requiring full specifiers including extensions; tsc doesn't rewrite .ts to .js in imports, so you must write .js to match emitted files, and the new resolution mode maps .js specifiers to .ts source files.
Journey Context:
You're migrating to TypeScript 4.7\+ native ESM \('type': 'module' in package.json\). You write import \{ foo \} from './foo.ts'. TypeScript says you can't import TS with extension. You remove the extension: import \{ foo \} from './foo'. It compiles, but Node.js ESM complains 'Cannot find module './foo' - add .js extension'. You try import \{ foo \} from './foo.js'. TypeScript complains 'Cannot find module './foo.js' or its corresponding type declarations'. You're stuck. You research the TypeScript 4.7 release notes about ESM module resolution. You realize you need 'moduleResolution': 'Node16' or 'NodeNext' in tsconfig. With this mode, TypeScript understands that in ESM, import specifiers must have extensions, and it creates a mapping: when you write ./foo.js in source, it looks for ./foo.ts on disk during type-checking, anticipating that the emitted file will be .js. This aligns with the ESM spec where TypeScript files become JS but imports must reference the output. You update tsconfig to 'module': 'NodeNext', 'moduleResolution': 'NodeNext', and change all imports to use .js extensions. Type-checking passes, the compiled JS has .js extensions, and Node.js runs it without errors.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-21T16:17:28.220486+00:00— report_created — created