import =

Binds a CommonJS require or an existing namespace to a local name that works in both type and value positions.

Signature
import ident = require("mod");
import ident = A.B.C;
export import ident = require("mod");
  • Added in TypeScript 1.0

Parameters

ParameterTypeDescription
$identidentifierThe local name bound to the module's export assignment. Unlike `const x = require(...)`, this binding carries both the value and any types the module exports.
$require("mod")module specifier callThe CommonJS require form. Emits `const ident = require("mod")` under a CommonJS target and is preserved as written under `--module preserve`.
$A.B.Coptionalentity nameThe alias form: binds a shorter local name to an existing namespace, class or enum. No module resolution happens and nothing is imported.
$export (modifier)optionalkeywordRe-exports the alias from the current module or namespace.

Errors

Raises "import ... = require(...) is not allowed when '--module' is set to 'esnext'" (or any ES module target/format), and "An import declaration can only be used at the top level of a namespace or module" when nested elsewhere.

Changelog

  • PHP 2.7`esModuleInterop` added, making `import x from "cjs-mod"` work against `export =` modules and removing most reasons to write `import =`.
  • PHP 4.7Under `--module node16`/`nodenext`, `import =` is rejected in ES module files (`.mts`, or `.ts` under `"type": "module"`), because it has no ESM meaning.
  • PHP 5.0`verbatimModuleSyntax` requires `import =` in CommonJS-format files: ES `import` syntax is no longer rewritten to `require`.

import x = require("mod") is TypeScript's pre-ES2015 import. It exists because const x = require("mod") creates a value binding only, so a module that publishes a type through export = cannot be consumed that way — const Options = require("./a") gives you a value named Options, not a type you can annotate with. The import = form binds both meanings at once.

When to reach for it

  • A dependency uses export = and you have esModuleInterop off. This is the only way to import it cleanly.
  • A CommonJS-format file under verbatimModuleSyntax. That flag stops rewriting ES import into require, so CJS files must say import = explicitly.
  • Aliasing a nested namespace, e.g. import Shapes = Geometry.TwoD.Shapes, which involves no module at all.

What trips people up

Most projects today set esModuleInterop: true, which synthesises a default export for CommonJS modules and makes import x from "mod" work. Once that is on, import = is rarely needed — and it becomes an outright error the moment the file is treated as ESM, which is what happens when you add "type": "module" to package.json under --module nodenext.

Example

Example · typescript
// node_modules/legacy-logger/index.d.ts
declare function log(msg: string): void;
declare namespace log {
  interface Options { level: "debug" | "info" }
}
export = log;

// app.ts  (CommonJS format)
import log = require("legacy-logger");

const opts: log.Options = { level: "debug" }; // type side
log(`level=${opts.level}`);                    // value side
console.log(opts);
// -> { level: "debug" }

// Emits: const log = require("legacy-logger");

When to use it

  • Consume a package that declares export = while esModuleInterop is turned off
  • Write a CommonJS .cts file under verbatimModuleSyntax where import/export is no longer rewritten
  • Alias a deeply nested namespace to a short local name inside a declaration file
  • Import a legacy typings package whose root export is a callable function with attached types

Frequently asked questions

When should I use import = require() instead of a normal import?

Use it when the module publishes its API through `export =` and `esModuleInterop` is off, or when you are in a CommonJS-format file under `verbatimModuleSyntax`, which no longer rewrites ES import syntax into require. In every other modern setup, turn on `esModuleInterop` and write a normal default import.

Why does import = require() error under module esnext or nodenext?

Because the form has no ES module equivalent — it compiles to a synchronous `require` call, which does not exist in ESM. TypeScript rejects it in any file it treats as an ES module, including `.mts` files and `.ts` files in a package with `"type": "module"`.

What is the difference between import x = require("m") and const x = require("m")?

`import x = require("m")` binds both the value and the types the module exports, so `x.SomeType` works as an annotation; `const x = require("m")` creates a value binding only, typed `any` unless you annotate it, and gives you no access to exported types.

More examples

The same import with esModuleInterop on

With esModuleInterop the compiler synthesises a default export for the `export =` module and emits an interop helper, so the standard `import ... from` form binds the callable. This is the version you should write in new code; the `import =` form is what you fall back to when the flag is off or the file is explicitly CommonJS under verbatimModuleSyntax.

Example · typescript
// tsconfig.json: { "compilerOptions": { "esModuleInterop": true, "module": "commonjs" } }

// node_modules/legacy-logger/index.d.ts
declare function log(msg: string): void;
declare namespace log {
  interface Options { level: "debug" | "info" }
}
export = log;

// app.ts — ordinary ESM syntax now works
import log from "legacy-logger";
import type { Options } from "legacy-logger";

const opts: Options = { level: "info" };
log(`level=${opts.level}`);
console.log(opts.level);
// -> "info"

Namespace aliasing — no module involved

Here `import =` performs no module resolution at all — it is a local alias for a namespace that already exists in scope, and `Flat` works as both a type namespace (`Flat.Point`) and a value (`Flat.distance`). A `const Flat = Geometry.TwoD` would give you the function but leave `Flat.Point` unresolvable as a type.

Example · typescript
namespace Geometry {
  export namespace TwoD {
    export interface Point { x: number; y: number }
    export function distance(a: Point, b: Point): number {
      return Math.hypot(a.x - b.x, a.y - b.y);
    }
  }
}

// Alias form: binds an existing entity, emits `var Flat = Geometry.TwoD;`
import Flat = Geometry.TwoD;

const a: Flat.Point = { x: 0, y: 0 };
const b: Flat.Point = { x: 3, y: 4 };
console.log(Flat.distance(a, b));
// -> 5

Discussion

  • Be the first to comment on this lesson.