Report #6130
[bug\_fix] TS2349: This expression is not callable. Type 'typeof import\("axios"\)' has no call signatures \(or similar for other CJS modules imported via ESM\)
This occurs when using ES Modules \(\`'module': 'NodeNext'\`\) to import a CommonJS module that exports a single function via \`module.exports = fn\`. In Node.js ESM, when you write \`import pkg from 'cjs'\`, the \`pkg\` binding is the Module Namespace Object. For CJS modules, this namespace object contains the \`module.exports\` as the \`default\` export only if the CJS module has an \`\_\_esModule\` property or if TypeScript's \`esModuleInterop\` is used and the runtime supports it. However, with native Node.js ESM resolution \(\`NodeNext\`\), if the CJS module does \`module.exports = fn\`, the namespace object has that function as its \`default\` export, but TypeScript's strict ESM analysis may not recognize it as callable directly via a default import without \`esModuleInterop\` being effectively active at runtime, or the error occurs because the developer used \`import \* as pkg\` which creates a non-callable namespace. The fix is to use the namespace import: \`import \* as pkg from 'axios'\`, then call \`pkg.default\(\)\` if necessary, or ensure \`esModuleInterop\` is enabled and use \`import pkg from 'axios'\` understanding that Node.js will look for the \`default\` export on the CJS namespace. For maximum compatibility with native Node.js ESM and CJS, using \`import \* as axios from 'axios'\` is safest, or using \`import axios from 'axios'\` only if the package provides proper ESM exports or \`esModuleInterop\` is handled by the loader.
Journey Context:
A developer migrates to ES Modules, setting \`'type': 'module'\` and \`'module': 'NodeNext'\`. They install \`axios\` \(a CommonJS package\) and write \`import axios from 'axios'\`. VS Code shows no error, and \`tsc --noEmit\` passes because \`esModuleInterop\` is enabled in their config. However, running \`node dist/index.js\` crashes: \`TypeError: axios is not a function\`. The developer debugs and finds \`console.log\(axios\)\` shows \`\{ default: \[Function: axios\], ... \}\`. They realize the function is under the \`default\` property. They try \`import \* as axios from 'axios'\`, which gives them the namespace object. Now \`axios.default\` works, but \`axios\(\)\` does not \(namespace is not callable\). The 'aha' moment is understanding that in Node.js native ESM, \`import pkg from 'cjs'\` creates a binding to the Module Namespace Object, and the \`default\` export of that namespace is the \`module.exports\` of the CJS module. However, for the call signature to work directly with \`pkg\(\)\`, the package must have an \`\_\_esModule: true\` marker or the runtime must support synthetic default imports. The reliable fix for native Node.js ESM with CJS modules is to either use \`import \* as pkg from 'pkg'\` and then call \`pkg.default\(\)\`, or to use a default import \`import pkg from 'pkg'\` only if the package has proper ESM exports or the project uses a loader that handles the interop. The developer fixes it by switching to \`import \* as axios from 'axios'\` and using \`axios.default\` or by enabling \`esModuleInterop\` and ensuring they use a compatible loader, or simply by using \`import axios from 'axios'\` and accessing \`axios\` directly if the package's types are correctly set up for ESM default imports.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-15T23:14:12.318147+00:00— report_created — created