How to fix Import assignment cannot be used when targeting ECMAScript modules in TypeScript
This TypeScript error occurs when using CommonJS-style import assignment syntax (`import x = require('module')`) while targeting ECMAScript modules (ESM). The fix involves switching to ES module syntax (`import x from 'module'`) or adjusting your TypeScript configuration to target CommonJS modules.
Quick fix
Fixes TS1202
// BEFORE - CommonJS import assignment (causes error)
import fs = require("fs");
import express = require("express");
import * as path from "path"; // This is actually ES module syntax
// AFTER - ES module syntax (correct for ES modules)
import fs from "fs";
import express from "express";
import * as path from "path"; // This is already correct
// For default exports:
import React from "react";
import lodash from "lodash";
// For named exports:
import { useState } from "react";
import { debounce } from "lodash";
// For namespace imports (already ES module syntax):
import * as fs from "fs"; // Alternative to default import- Switch to ES module import syntax
- Check and update your tsconfig.json module setting
- Handle libraries that use export = syntax
- Update package.json for ES modules
// BEFORE - CommonJS import assignment (causes error)import fs = require("fs");import express = require("express");import * as path from "path"; // This is actually ES module syntax// AFTER - ES module syntax (correct for ES modules)import fs from "fs";import express from "express";import * as path from "path"; // This is already correct// For default exports:import React from "react";import lodash from "lodash";// For named exports:import { useState } from "react";import { debounce } from "lodash";// For namespace imports (already ES module syntax):import * as fs from "fs"; // Alternative to default importImport assignment cannot be used when targeting ECMAScript modules
What this error means
The "Import assignment cannot be used when targeting ECMAScript modules" error (TS1202) appears when you try to use TypeScript's CommonJS-style import assignment syntax in a context that targets ECMAScript modules (ESM). TypeScript supports two main module syntaxes: 1. **CommonJS syntax**: Uses `import x = require("module")` and `export =` for compatibility with Node.js's traditional module system 2. **ES Module syntax**: Uses `import x from "module"` and `export default` following the ECMAScript standard When your TypeScript configuration targets ES modules (via the `module` compiler option set to `es2015`, `es2020`, `es2022`, or `esnext`), you must use ES module syntax throughout your code. The CommonJS import assignment syntax is not valid in ES modules because it doesn't exist in the ECMAScript specification. This error commonly occurs when: - Migrating a project from CommonJS to ES modules - Using mixed syntax in a project targeting ES modules - Copying code from older TypeScript examples or tutorials - Working with libraries that export using `export =` syntax in an ES module context
Typical symptoms
- Compilation fails with 'Import assignment cannot be used when targeting ECMAScript modules'
- Error appears on lines using `import x = require('module')` syntax
- Error code TS1202 appears in compiler output
- Code works when targeting CommonJS but fails when targeting ES modules
- IDE shows red underlines on import assignment statements
- Build succeeds locally but fails in CI with different TypeScript configurations
Common causes
- Using `import x = require('module')` syntax with `module` set to ES module format in tsconfig.json
- Mixing CommonJS and ES module syntax in the same project
- Copying code from older TypeScript examples that use import assignment
- Library uses `export =` syntax but you're importing it in an ES module context
- File has `.mts` extension or `"type": "module"` in package.json but uses CommonJS syntax
- Project recently migrated from CommonJS to ES modules but not all imports were updated
- Using `--module esnext` or similar ES module target with import assignment syntax
How to fix "Import assignment cannot be used when targeting ECMAScript modules"
Replace all instances of CommonJS import assignment with ES module syntax:
// BEFORE - CommonJS import assignment (causes error)
import fs = require("fs");
import express = require("express");
import * as path from "path"; // This is actually ES module syntax
// AFTER - ES module syntax (correct for ES modules)
import fs from "fs";
import express from "express";
import * as path from "path"; // This is already correct
// For default exports:
import React from "react";
import lodash from "lodash";
// For named exports:
import { useState } from "react";
import { debounce } from "lodash";
// For namespace imports (already ES module syntax):
import * as fs from "fs"; // Alternative to default importAfter making these changes, recompile your TypeScript code:
npx tsc
# or
npm run buildThe error should be resolved if you're targeting ES modules.
Verify your TypeScript configuration and ensure consistency:
// tsconfig.json - For ES modules (modern projects)
{
"compilerOptions": {
"module": "es2022", // or "esnext", "es2020", "es2015"
"moduleResolution": "node16", // or "bundler", "node"
"target": "es2022",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true
}
}If you need to use CommonJS import assignment syntax, change to CommonJS target:
// tsconfig.json - For CommonJS (Node.js compatibility)
{
"compilerOptions": {
"module": "commonjs", // Allows import x = require()
"moduleResolution": "node",
"target": "es2022",
"esModuleInterop": true
}
}After changing tsconfig.json, restart your TypeScript server/IDE.
Some older libraries use export = syntax. TypeScript provides special handling for these:
// Library uses: export = MyClass
// In CommonJS projects:
import MyClass = require("old-library");
// In ES module projects, use esModuleInterop:
import MyClass from "old-library";
// Requires: "esModuleInterop": true in tsconfig.json
// Alternative: Use a namespace import
import * as OldLibrary from "old-library";
const MyClass = OldLibrary.default || OldLibrary;Enable esModuleInterop in your tsconfig.json:
{
"compilerOptions": {
"esModuleInterop": true,
"allowSyntheticDefaultImports": true
}
}This allows ES module syntax to work with CommonJS modules that use export =.
If targeting ES modules, ensure your package.json is configured correctly:
// package.json - For ES modules
{
"type": "module", // Tells Node.js to treat .js files as ES modules
"exports": {
".": {
"import": "./dist/index.js",
"require": "./dist/index.cjs"
}
},
"scripts": {
"build": "tsc",
"start": "node dist/index.js"
}
}For mixed modules (both .js and .cjs files):
{
"type": "module", // .js files are ES modules
// .cjs files are CommonJS modules
}File extensions matter in ES modules:
- .mjs - ES module (always)
- .cjs - CommonJS module (always)
- .js - Depends on "type" field in package.json
If you need conditional imports, use dynamic import() instead of require():
// BEFORE - CommonJS conditional require (won't work in ES modules)
let module;
if (condition) {
module = require("module-a");
} else {
module = require("module-b");
}
// AFTER - ES module dynamic import
let module;
if (condition) {
module = await import("module-a");
} else {
module = await import("module-b");
}
// For synchronous-like behavior with top-level await:
const module = condition
? await import("module-a")
: await import("module-b");Note: import() returns a Promise, so you need async/ await or .then().
Dynamic imports work in both ES modules and CommonJS (when targeting ES2015+).
When migrating from CommonJS to ES modules, update all files consistently:
# 1. Update tsconfig.json
{
"compilerOptions": {
"module": "es2022",
"moduleResolution": "node16"
}
}
# 2. Update package.json
{
"type": "module"
}
# 3. Convert all import statements
# Use a script or search/replace:
sed -i 's/import \(.*\) = require("\(.*\)")/import \1 from "\2"/g' src/**/*.ts
# 4. Update export statements
# Change:
# export = MyClass
# To:
# export default MyClass
# 5. Add .js extensions to relative imports (for Node16/NodeNext)
import { helper } from "./helper.js"; // Not "./helper"
# 6. Test thoroughly
npm run build
npm testConsider using tools like ts-migrate or @typescript-eslint plugin to automate migration.
Advanced notes
### TypeScript Module Target Compatibility
TypeScript's module compiler option determines the module syntax in emitted JavaScript:
| Module Target | Output Syntax | Import Assignment Allowed |
|---------------|---------------|---------------------------|
| commonjs | require()/module.exports | ✅ Yes |
| amd | AMD define() | ✅ Yes |
| umd | UMD wrapper | ✅ Yes |
| system | SystemJS | ✅ Yes |
| es2015/es2020/es2022/esnext | import/export | ❌ No |
| node16/node18/nodenext | Mixed (depends on file extension) | Only in .cjs/.cts files |
### File Extension Rules with Node16/NodeNext
When using --module node16/node18/nodenext, TypeScript treats files differently based on extensions:
- .mts/.mjs: ES modules - cannot use import x = require()
- .cts/.cjs: CommonJS modules - can use import x = require()
- .ts/.js: Depends on nearest package.json "type" field
Example with mixed extensions:
// index.mts (ES module)
import fs from "fs"; // ✅
import fs = require("fs"); // ❌ Error
// utils.cts (CommonJS module)
import fs = require("fs"); // ✅
import fs from "fs"; // ✅ with esModuleInterop### Historical Context
The import x = require() syntax was introduced in TypeScript to provide a 1:1 mapping between TypeScript and CommonJS output. Before ES modules were widely supported, this was the primary way to import CommonJS modules in TypeScript.
With the adoption of ES modules as the JavaScript standard, TypeScript now encourages using ES module syntax (import/export) for new projects. The esModuleInterop flag bridges the gap between CommonJS and ES module interoperability.
### Module Resolution Differences
ES modules and CommonJS have different resolution algorithms:
CommonJS:
- Looks for node_modules from current directory up to root
- Can omit file extensions (tries .js, .json, .node)
- Case-insensitive on Windows
ES Modules:
- Requires file extensions for relative imports (except with bundlers)
- Uses import.meta.resolve() for programmatic resolution
- Follows package.json "exports" field strictly
### Testing Module Compatibility
To test if your code works in both systems during migration:
# Build as ES modules
npx tsc --module es2022 --outDir dist-esm
# Build as CommonJS
npx tsc --module commonjs --outDir dist-cjs
# Test both outputs
node dist-esm/index.js
node dist-cjs/index.js### Tools for Migration
- ts-migrate: Facebook's tool for TypeScript migration
- @typescript-eslint: ESLint rules to enforce module consistency
- jscodeshift: Codemod framework for large-scale refactoring
- tsc --noEmit: Type check without building to catch errors early
### Common Pitfalls
1. Circular dependencies: ES modules handle these differently than CommonJS
2. Dynamic require: require(someVariable) must become import(someVariable)
3. __dirname/`__filename`: Use import.meta.url and fileURLToPath() in ES modules
4. require.cache: No direct equivalent in ES modules
5. Conditional imports: Must use dynamic import() in ES modules
Frequently asked questions
- What does "TS1202" mean?
- The "Import assignment cannot be used when targeting ECMAScript modules" error (TS1202) appears when you try to use TypeScript's CommonJS-style import assignment syntax in a context that targets ECMAScript modules (ESM). TypeScript supports two main module syntaxes: 1. js's traditional module system 2.
- How do I fix "TS1202"?
- This TypeScript error occurs when using CommonJS-style import assignment syntax (import x = require('module')) while targeting ECMAScript modules (ESM). The fix involves switching to ES module syntax (import x from 'module') or adjusting your TypeScript configuration to target CommonJS modules.
- What causes "TS1202"?
- Using import x = require('module') syntax with module set to ES module format in tsconfig.json Mixing CommonJS and ES module syntax in the same project Copying code from older TypeScript examples that use import assignment
- Is the "TS1202" error serious?
- It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.
Official resources & further reading
- 📚TypeScript Official Documentation
- 📖TypeScript Handbook - Modules(Official Docs)
- 📖TypeScript Handbook - Modules Reference(Official Docs)
- 📖TypeScript TSConfig - module(Official Docs)
- 📖TypeScript TSConfig - esModuleInterop(Official Docs)
- 🐙