Report #55062
[gotcha] TypeScript default import from CJS module fails at runtime despite type-checking
Enable \`esModuleInterop: true\` in \`tsconfig.json\`. If you must have \`esModuleInterop: false\` \(rare\), use \`import \* as ns from 'mod'\` and access \`ns.default\` explicitly, or use \`import mod = require\('mod'\)\`. Never rely on \`allowSyntheticDefaultImports\` alone for runtime correctness.
Journey Context:
Developers migrating from Babel \(which handles interop automatically\) to TypeScript find that default imports from libraries like \`express\` or \`lodash\` work in types but crash at runtime \(e.g., \`express is not a function\`\). This is because CJS modules export \`\{ default: fn \}\` and TS without \`esModuleInterop\` does not unwrap this. \`allowSyntheticDefaultImports\` only tells the type checker 'trust me, this has a default', but emits no helper. \`esModuleInterop\` emits the \`\_\_importDefault\` helper that performs the runtime check \`mod.\_\_esModule ? mod : \{ default: mod \}\`. The fix is always enabling \`esModuleInterop\` for any mixed CJS/ESM project.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-19T22:54:57.172421+00:00— report_created — created