This TypeScript error occurs when using namespace imports (import * as) with modules that lack proper export structure. Enable esModuleInterop or switch to named imports to resolve.
This error appears when you attempt to use a namespace import (wildcard import) syntax like `import * as Name from "module"` with a module that doesn't export members in a way that's compatible with this import style. TypeScript's module system expects namespace imports to receive an object containing all named exports from the module. However, some modules—particularly CommonJS modules or those with only default exports—don't provide this structure. When TypeScript detects this mismatch between import style and module export structure, it raises this error. The issue is fundamentally about the incompatibility between ES module syntax (`import * as`) and CommonJS module formats (`module.exports`). By default, TypeScript treats namespace imports strictly according to the ES6 spec, where the imported namespace must be an object. CommonJS modules often export primitives, functions, or classes directly, which violates this expectation.
The most comprehensive fix is to enable the esModuleInterop compiler option, which adds helper functions to make ES module syntax compatible with CommonJS modules.
Open your tsconfig.json and add or modify the compiler options:
{
"compilerOptions": {
"esModuleInterop": true,
"allowSyntheticDefaultImports": true
}
}Note that esModuleInterop automatically enables allowSyntheticDefaultImports, so you can omit the second option if preferred. After making this change, recompile your project:
npx tsc --build --forceThis flag modifies both the type checking behavior and the emitted JavaScript to properly handle the mismatch between import styles.
If the module exports a default export, change from namespace import to default import syntax.
Before:
import * as moment from 'moment';After:
import moment from 'moment';This works when the module has a default export and you've enabled esModuleInterop or allowSyntheticDefaultImports. Use your editor's auto-import feature (Ctrl/Cmd + Space in most editors) to verify the correct import style for the module.
If the module exports specific named members, import only what you need instead of using a namespace import.
Before:
import * as utils from './utils';
utils.formatDate();After:
import { formatDate } from './utils';
formatDate();This approach is more explicit and works with any module that properly exports named members. To discover available exports, place your cursor inside the curly braces and trigger autocomplete (Ctrl + Space).
Check if the module has correct type definitions. For third-party packages, ensure you have the corresponding @types package installed:
npm install --save-dev @types/package-nameIf the types are outdated or incorrect, you may need to update them:
npm update @types/package-nameYou can also inspect the type definitions to understand the export structure:
# Navigate to the type definition file
code node_modules/@types/package-name/index.d.tsLook for export default, export =, or named export statements to determine the correct import syntax.
If you cannot modify TypeScript configuration and need to import a CommonJS module, you can use require with proper typing:
const moment = require('moment');
// or with typing
import moment = require('moment');However, this approach ties your code to CommonJS and may cause issues with ES module bundlers. Only use this as a temporary workaround when other solutions aren't viable.
Note: If using "module": "esnext" or similar in tsconfig.json, import = syntax may not be allowed. In that case, stick with enabling esModuleInterop instead.
Why esModuleInterop Matters
The esModuleInterop flag serves two purposes:
1. Type checking: Allows TypeScript to accept default imports from CommonJS modules
2. Emit helpers: Adds __importStar and __importDefault helper functions to the compiled JavaScript that properly handle the namespace object creation
Without this flag, TypeScript strictly enforces ES6 module semantics, where import * as x must receive an object containing the module's exports. CommonJS modules that use module.exports = value don't fit this pattern.
allowSyntheticDefaultImports vs esModuleInterop
allowSyntheticDefaultImports only affects type checking—it doesn't change the emitted JavaScript. Using it alone can allow code that compiles but fails at runtime. Always prefer esModuleInterop, which handles both type checking and runtime behavior.
Package.json "type": "module" Considerations
If your package.json specifies "type": "module", you're using native ES modules. In this case, you cannot mix CommonJS require() syntax. Stick with import statements and ensure esModuleInterop is enabled for third-party CommonJS dependencies.
Webpack and Bundler Compatibility
Modern bundlers like webpack, Vite, and esbuild handle module interop automatically at build time. If you're using a bundler, enabling esModuleInterop in TypeScript ensures your source code type-checks correctly without affecting the bundler's module resolution strategy.
Function expression requires a return type
Function expression requires a return type
Value of type 'string | undefined' is not iterable
How to fix "Value is not iterable" in TypeScript
Type 'undefined' is not assignable to type 'string'
How to fix "Type undefined is not assignable to type string" in TypeScript
Type narrowing from typeof check produces 'never'
How to fix "Type narrowing produces never" in TypeScript
Type parameter 'T' has conflicting constraints
How to fix "Type parameter has conflicting constraints" in TypeScript