Many npm packages are CommonJS and declare their types with export =. Importing one as import express from "express" needs the checker to accept a default import that the package does not literally have. allowSyntheticDefaultImports allows that in the types; esModuleInterop also changes the JavaScript tsc writes, and turns on allowSyntheticDefaultImports.
In a .js project you rarely touch either: a file named jsconfig.json defaults allowSyntheticDefaultImports to true, module: "nodenext" and "preserve" imply both, and moduleResolution: "bundler" implies allowSyntheticDefaultImports. The failing example has to switch it off on purpose (TS1259).
At a glance
| Write | Nothing: a jsconfig.json turns on allowSyntheticDefaultImports, and nodenext and preserve imply both |
|---|---|
| On the command line | --esModuleInterop |
| Type | boolean |
| Default in TypeScript 5.9 | false (on when module is node16 to nodenext, or preserve) |
| Default from TypeScript 6.0 | true, and false can no longer be set (TypeScript 6.0) |
| TypeScript's description | Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. |
The description is quoted from TypeScript's own option list (Apache-2.0); see the notices.
Examples
import express from "express";error TS1259: Module '"/project/node_modules/@types/express/index"' can only be default-imported using the 'allowSyntheticDefaultImports' flag
export const app = express();jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "esnext",
"types": [],
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": false
},
"include": [
"*.js"
]
} import express from "express";
export const app = express();jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "esnext",
"types": [],
"moduleResolution": "bundler"
},
"include": [
"*.js"
]
} Why esModuleInterop is mostly about output
The "additional JavaScript" in TypeScript's description is the helper code tsc adds when it writes CommonJS output. With noEmit there is no output, so for a checked JavaScript project only the type side matters, which is allowSyntheticDefaultImports.
Under TypeScript 6.0 and 7.0
Every example on this page also runs under TypeScript 6.0.3 and 7.0.2 when this site is built. One of them reports something different from 5.9.3, the version the page describes; the ✗ on its badge links to its entry below.
allowSyntheticDefaultImports: false is deprecated, then removed
The pattern: A config that sets allowSyntheticDefaultImports (or esModuleInterop) to false, and a default import of a CommonJS package such as Express.
Why: Per the 6.0 release notes these options can no longer be false: 6.0 reports the setting as deprecated (TS5107) and still applies it, so the default import keeps its TS1259; 7.0 reports it as removed (TS5108) and always uses the safer interop, so the default import works and TS1259 is gone. Source: TypeScript 6.0 release notes: Deprecated: --esModuleInterop false and --allowSyntheticDefaultImports false.
allowSyntheticDefaultImports off: a default import of a CommonJS package main.js
- TypeScript 5.9.3
main.js(2,8): error TS1259: Module '"/project/node_modules/@types/express/index"' can only be default-imported using the 'allowSyntheticDefaultImports' flag
- TypeScript 6.0.3differs from 5.9.3
error TS5107: Option 'allowSyntheticDefaultImports=false' is deprecated and will stop functioning in TypeScript 7.0. Specify compilerOption '"ignoreDeprecations": "6.0"' to silence this error. main.js(2,8): error TS1259: Module '"/project/node_modules/@types/express/index"' can only be default-imported using the 'allowSyntheticDefaultImports' flag
- TypeScript 7.0.2differs from 5.9.3
jsconfig.json(10,37): error TS5108: Option 'allowSyntheticDefaultImports=false' has been removed. Please remove it from your configuration.
What works in every version: Remove the option (or set esModuleInterop: true) and keep the default import.
import express from "express";
export const app = express();jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "esnext",
"types": ["node"],
"moduleResolution": "bundler",
"esModuleInterop": true
},
"include": ["*.js"]
} Gotchas
- Under
nodenext, what a default import of a CommonJS package gives you follows Node.js: the wholemodule.exportsobject. A package whose types were written for bundler interop can look different there; trust what Node.js does at run time.
Sources
- TSConfig Reference: esModuleInterop · TypeScript
- TSConfig Reference: allowSyntheticDefaultImports · TypeScript
- TypeScript 2.7 release notes · TypeScript handbook
- TypeScript 1.8 release notes · TypeScript handbook
- TypeScript 6.0 release notes · TypeScript handbook
Last reviewed 20 October 2026.