tezvyn:

What problem does esModuleInterop solve for CommonJS imports?

AI-drafted, machine-checkedSource: typescriptlang.orgintermediate

Tests CommonJS-to-ESM interop. A strong answer covers: default imports for CommonJS modules; runtime helpers checking __esModule and wrapping exports; and implicit allowSyntheticDefaultImports. Red flag: claiming it is purely type-level with no emit impact.

WHAT THIS TESTS: This question tests whether you understand the difference between CommonJS and ES module semantics in TypeScript, specifically how the compiler bridges the two at both the type-checking and emit layers. The interviewer wants to see that you know why a plain default import fails against a CommonJS module and what TypeScript does to fix it.

A GOOD ANSWER COVERS: First, the core problem: CommonJS modules typically export via module.exports or exports.foo, which means they have no ES module default export. Without esModuleInterop, writing import foo from "foo" against a CommonJS library either errors or yields undefined at runtime, even if type definitions exist. Second, the runtime mechanism: when enabled, TypeScript emits a helper like __importDefault that checks whether the required module already has an __esModule property. If it does not, the helper wraps the entire module in an object shaped as { default: module }. Third, the type-checking side effect: enabling esModuleInterop automatically sets allowSyntheticDefaultImports to true, so the compiler accepts the default import syntax during type checking. Fourth, the practical outcome: you can write clean ESM-style imports for npm packages that still ship CommonJS, and the emitted JavaScript runs correctly in Node or a bundler.

COMMON WRONG ANSWERS: A red flag is saying esModuleInterop is only a type-level flag with no impact on emitted JavaScript. Another mistake is confusing it with isolatedModules or verbatimModuleSyntax. Some candidates claim it converts CommonJS to ESM, which is false; it only adds a thin runtime adapter. Finally, stating that you must use import * as foo without explaining why esModuleInterop removes that requirement shows shallow understanding.

LIKELY FOLLOW-UPS: The interviewer may ask what happens if you disable esModuleInterop but keep allowSyntheticDefaultImports. The correct distinction is that allowSyntheticDefaultImports only loosens type checking, while esModuleInterop also changes emit. They might also ask about the __esModule property and why Babel-set code often includes it, or how moduleResolution affects the lookup.

ONE CONCRETE EXAMPLE: Imagine importing lodash, which ships CommonJS. Without esModuleInterop, import _ from "lodash" compiles to var lodash_1 = require("lodash"), and lodash_1.default is undefined. With esModuleInterop, the emit becomes var lodash_1 = __importDefault(require("lodash")), which returns { default: require("lodash") } when __esModule is missing, so lodash_1.default works as expected.

Read the original → typescriptlang.org

Get five bites like this every day.

Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.