Report #69854
[bug\_fix] TS5042: Option 'module' must be set to 'NodeNext' when option 'moduleResolution' is set to 'NodeNext', or ESM import errors with '.ts' extensions
Set 'module' to 'NodeNext' \(or 'Node16'\) and 'moduleResolution' to 'NodeNext'. When using TypeScript 4.7\+ with ESM, write imports with '.js' extensions even for '.ts' files \(e.g., \`import './foo.js'\` for 'foo.ts'\), OR use 'moduleResolution': 'bundler' if using a bundler that handles '.ts' extensions, OR enable 'allowImportingTsExtensions' with 'noEmit' or 'emitDeclarationOnly' \(for type-checking only setups\). Root cause: Node.js ESM requires explicit file extensions in imports. TypeScript doesn't rewrite extensions in emit, so source must use the extension that will exist at runtime \(.js for compiled .ts\). 'NodeNext' enforces this alignment.
Journey Context:
Developer starts a new Node.js project with 'type': 'module' in package.json. They set 'module': 'ESNext' and 'moduleResolution': 'node' in tsconfig.json. They write \`import \{ helper \} from './helper.ts'\`. TypeScript complains: 'An import path can only end with a '.ts' extension when 'allowImportingTsExtensions' is enabled.' They enable 'allowImportingTsExtensions' but get 'Option allowImportingTsExtensions can only be used when either noEmit or emitDeclarationOnly is enabled.' They switch to 'moduleResolution': 'bundler' which allows .ts extensions, but when they run 'tsc' to emit JS, the output still has '.ts' extensions in the import statements, causing Node.js to crash at runtime with 'Cannot find module ./helper.ts'. The rabbit hole reveals that TypeScript never rewrites import specifiers during emit \(except for some path mapping in non-ESM\). With NodeNext module resolution, TypeScript enforces that you write '.js' extensions in your TS source code, because that's what Node ESM will resolve to the adjacent .ts file at runtime via the loader. The fix is to use './helper.js' in the import statement even though the file is './helper.ts', and ensure 'module' and 'moduleResolution' are set to 'NodeNext' \(or 'Node16'\).
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-20T23:44:06.153792+00:00— report_created — created