export =
Declares that a module's entire export is one value, matching CommonJS module.exports = x.
export = ident;- Added in TypeScript 1.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$ident | entity name | A single identifier naming the class, function, interface, namespace, enum or variable that becomes the module's whole export. It must be a name already declared in the file — an arbitrary expression is not allowed. |
Errors
Raises "An export assignment cannot be used in a module with other exported elements" if any `export` declaration coexists with it, and "An export assignment cannot be used in a module with ES module syntax" (or when targeting an ES module format).
Changelog
- PHP 2.7`esModuleInterop` added: consumers can now import an `export =` module with `import x from "mod"` rather than `import x = require("mod")`.
- PHP 4.7Under `--module node16`/`nodenext`, `export =` is rejected in ES module files, since ESM has no single-value export slot.
- PHP 5.0`verbatimModuleSyntax` emits `export = { ... }` verbatim as `module.exports = { ... }` instead of rewriting ES export syntax.
export = X says the module is X, not a namespace containing X. It is the type-system spelling of module.exports = X, and it is how most pre-ESM npm packages actually behave: require("express") returns a callable function, not an object with an express property.
When to reach for it
Nearly always when writing a .d.ts for an existing CommonJS package. If require("pkg") hands back a function or a class directly, export = is the only accurate description; export default would be wrong, because it describes a module with a default property.
It also solves a problem module.exports = cannot: the exported entity may be an interface or a namespace. module.exports = Options fails when Options is type-only, while export = Options is fine.
What trips people up
It is exclusive. One export = and every other export in the file is an error, which is why you attach extra members via declaration merging — a function plus a namespace of the same name — rather than by adding exports.
On the consuming side, whether import x from "mod" works depends on esModuleInterop. With it off, only import x = require("mod") compiles.
Example
// counter.ts
class Counter {
constructor(public value = 0) {}
increment(): this { this.value++; return this; }
}
export = Counter; // emits: module.exports = Counter;
// app.ts
import Counter = require("./counter");
const c = new Counter(41);
console.log(c.increment().value);
// -> 42
const typed: Counter = c; // the name works as a type too
console.log(typed.value);
// -> 42When to use it
- Write typings for an npm package whose require() returns a function or class directly
- Describe a module whose sole export is an interface, which module.exports cannot express
- Author a .cts file under verbatimModuleSyntax that must emit module.exports =
- Attach option types to a callable export via function/namespace declaration merging
Frequently asked questions
What is the difference between export = and export default?
`export =` makes the module itself equal to one value, matching `module.exports = x` in CommonJS; `export default` creates a module object with a `default` property. They emit different JavaScript, and a consumer importing an `export =` module with default syntax needs `esModuleInterop` for the two to line up.
Why does export = say it cannot be used in a module with other exported elements?
Because the export assignment replaces the entire export object, so no other export can survive. Remove the other `export` keywords and, if you need extra members, merge them into the exported entity with a same-named namespace declaration.
Can I use export = in an ES module?
No. TypeScript rejects `export =` in any file it treats as ESM — a `.mts` file, or a `.ts` file in a package with `"type": "module"` under `--module node16`/`nodenext`. Use `export default` there, and reserve `export =` for CommonJS files and declaration files describing CommonJS packages.
More examples
A callable export with attached types, via declaration merging
Because `export =` allows exactly one exported entity, the options interface and the `defaults` constant ride along inside a namespace that merges with the function of the same name. Consumers reach them as `slugify.Options` and `slugify.defaults` — the shape real CommonJS packages have, where the module is a function that also carries properties.
// node_modules/slugify/index.d.ts
declare function slugify(input: string, options?: slugify.Options): string;
declare namespace slugify {
interface Options {
separator?: string;
lower?: boolean;
}
const defaults: Required<Options>;
}
export = slugify;
// app.ts
import slugify = require("slugify");
const opts: slugify.Options = { separator: "_", lower: true };
console.log(slugify("Hello World", opts));
// -> "hello_world"
console.log(slugify.defaults.separator);
// -> "-"What esModuleInterop changes for consumers
The same `export =` module is imported three ways. With esModuleInterop off, only the `import =` form compiles. With it on, TypeScript synthesises a default export and the ordinary default import works. The named import never works, because `export =` publishes no named members at all — a distinction that catches people who assume the export name is also a member name.
// mod.ts
interface Options { retries: number }
function connect(o: Options): string { return `retries=${o.retries}`; }
declare namespace connect { export { Options }; }
export = connect;
// consumer-a.ts — esModuleInterop: false
import connect = require("./mod");
console.log(connect({ retries: 3 }));
// -> "retries=3"
// consumer-b.ts — esModuleInterop: true
import connect2 from "./mod";
console.log(connect2({ retries: 5 }));
// -> "retries=5"
// consumer-c.ts — always an error: the module has no named export 'connect'
// import { connect as bad } from "./mod";
// tsc: Module '"./mod"' has no exported member 'connect'.
Discussion