TypeScript TS1384 is one of those errors that looks cryptic on first encounter but has a precise, fixable cause every time. It fires when the compiler finds an export modifier somewhere it cannot legally appear — specifically inside a module augmentation block. This guide explains exactly what TS1384 means, walks through every scenario that triggers it, and gives you the correct fix for each one.
What is TS1384?
TypeScript error TS1384 carries the message: "'export' modifier cannot be applied to a module augmentation." It fires when the compiler encounters an `export` keyword inside a `declare module` or `declare global` block — the two constructs TypeScript uses for module augmentation. Augmentation blocks are for extending the types of existing modules, not for declaring new public symbols, so export is structurally invalid inside them.
Module augmentation in one sentence
Module augmentation is the TypeScript mechanism that lets you add new members to an existing module's types — for example, adding a custom property to `Express.Request`, extending `Window` with a third-party global, or adding methods to a framework's component options. The syntax looks like a `declare module 'module-name' ` block, and it must live inside a module file (a file with at least one top-level `import` or `export` statement).
- TS1384 is a compiler error — the file will not type-check until it is fixed
- It does not affect runtime — the error is purely about type declarations
- It is deterministic — the same code always triggers it; there is no intermittent version
- It has a small set of root causes — script vs module context, isolatedModules, and incorrect .d.ts structure cover 95% of cases
Note
When TS1384 fires
The error always involves an `export` in a place where TypeScript does not allow one. There are three distinct patterns that produce it, and identifying which one applies to your codebase determines the correct fix.
Pattern 1 — export inside a declare module block
The most direct trigger: you write an `export` statement inside a `declare module` augmentation. The intent is usually to add something to the module's public API, but augmentation blocks do not work that way — they can only extend type declarations that already exist in the target module.
// ✗ TS1384 — export inside module augmentation
declare module 'some-library' {
export interface NewInterface { // <-- TS1384 fires here
id: string;
}
}
// ✓ Correct — interface added without export
declare module 'some-library' {
interface ExistingInterface {
newProperty: string; // extends the existing type
}
}Pattern 2 — file treated as a script, not a module
This is the most common cause of TS1384 in real projects. If a file has no top-level `import` or `export` statements, TypeScript treats it as a script with global scope. A `declare global ` block in a script file is meaningless — globals are already global in script files — so TypeScript rejects any `export` inside it with TS1384. The file needs to be a module for the augmentation context to make sense.
Pattern 3 — incorrect .d.ts file structure
Declaration files (`.d.ts`) that mix ambient module declarations with regular export statements in the wrong order can trigger TS1384. A `.d.ts` file that starts with top-level `export` declarations and then contains a `declare module` block is treated as a module, which is correct. But a `.d.ts` that wraps everything inside a single `declare module` block and then tries to use `export` inside that block confuses the augmentation context with a module definition context.
Warning
How to fix TS1384: module augmentation
The right fix depends on what you are actually trying to accomplish. There are two different goals that can lead to TS1384, and they require different approaches. Work through these four steps to resolve the error cleanly.
Identify which pattern is triggering TS1384
Read the full compiler error carefully. Note the file extension (.ts vs .d.ts), the line number, and whether the `export` is inside a `declare module` block, a `declare global` block, or somewhere else. The surrounding code context tells you which of the three patterns above applies. If the error path starts with `node_modules/`, skip to the `skipLibCheck` fix — the other patterns do not apply.
Add export {} to convert the file to a module
If your file has a `declare module` or `declare global` block but no top-level imports or exports, add `export ` at the top. This single line converts the file from script context to module context, which is the required environment for augmentation syntax. The empty export does not add anything to the compiled output — it is purely a TypeScript context signal.
// Add this line to convert the file to a module
export {};
declare global {
interface Window {
myAnalytics: AnalyticsInstance;
}
}Move declare global into an existing module
An alternative to adding `export ` is placing the `declare global` block inside a file that already has real imports or exports — such as the entry point of a library, a feature module, or a shared utilities file. This approach keeps your type augmentations co-located with the code they extend, which can be easier to maintain than a dedicated global declarations file.
Remove export from inside the augmentation block
If you genuinely want to add a new exportable type to an existing module's public API, the augmentation block is the wrong place for it. Move the declaration outside the `declare module` block entirely. To augment an existing interface, use the same interface name without `export` inside the block — TypeScript merges it automatically via declaration merging.
JSON Formatter & Validator
Validate your tsconfig.json and package.json for syntax errors instantly in your browser — no TypeScript compiler required.
TS1384 and isolatedModules
The `isolatedModules` compiler option is enabled by default in Vite, Next.js, Create React App with Babel, and any project using esbuild or SWC. It requires every file to be independently transformable without cross-file type information, which adds constraints that amplify several TypeScript errors — including TS1384.
What isolatedModules restricts
| Construct | Without isolatedModules | With isolatedModules |
|---|---|---|
| `const enum` | ✓ Allowed everywhere | ✗ Only in .d.ts files |
| `export type` | ✓ Optional | ✓ Required for type-only re-exports |
| `import type` | ✓ Optional | ✓ Required for type-only imports |
| Ambient declarations | ✓ In any .ts file | ✓ Better in .d.ts files |
| Module augmentation | ✓ In module .ts files | ✓ Preferred in .d.ts files |
| Namespace re-exports | ✓ Allowed | ✗ Restricted |
The isolatedModules-specific fix
When `isolatedModules` is on and TS1384 appears on an augmentation in a regular `.ts` file, the cleanest fix is to move the augmentation into a `.d.ts` file. Declaration files are never transformed by esbuild or Babel — they are read only by the TypeScript compiler. This eliminates the isolatedModules constraint entirely for that augmentation.
// This pattern works correctly with isolatedModules
export {};
declare module 'express' {
interface Request {
userId?: string;
tenantId?: string;
}
}Tip
TS1384 in .d.ts files
Declaration files add their own nuances to TS1384. The rules for what is valid in a `.d.ts` file differ slightly from regular `.ts` files, and the patterns that produce TS1384 in declaration files are often less intuitive.
The ambient module definition vs augmentation distinction
A `.d.ts` file can contain two fundamentally different things that look similar but behave differently. An ambient module definition (`declare module 'name' ` in a script-context `.d.ts` with no imports or exports) defines all the types for a module from scratch — it is used to type untyped JavaScript libraries. A module augmentation (`declare module 'name' ` in a module-context `.d.ts` with an `export `) extends an existing module's types. The distinction matters because `export` is valid inside an ambient module definition but produces TS1384 inside a module augmentation.
Choosing the right .d.ts pattern
If your `.d.ts` file is defining types for an untyped library (e.g. typing a legacy jQuery plugin), keep it as a script-context file with no `export ` at the top. Use `export` freely inside the `declare module` block. If your `.d.ts` file is augmenting an existing typed library (e.g. adding a property to `Express.Request`), add `export ` at the top and remove any `export` from inside the `declare module` block.
Note
skipLibCheck as a last resort
When TS1384 fires on a path inside `node_modules` and you have no control over the package, add `"skipLibCheck": true` to your tsconfig.json. This tells TypeScript to skip type checking of all `.d.ts` files — including those in `node_modules`. It is a legitimate, widely-used configuration option, not a hack. The trade-off is that you lose type checking for library declaration files entirely, so genuinely broken types in dependencies will go unreported. Use `skipLibCheck` only when the alternative is blocking your build.
TS1384 in frameworks and bundlers
Different frameworks and build tools configure TypeScript differently, which means TS1384 can surface for slightly different reasons depending on your stack. Here is how the most common environments produce and resolve the error.
Next.js
Next.js enables `isolatedModules` automatically via its default tsconfig and uses SWC for transformation. The standard pattern for type augmentation in Next.js is a dedicated `types/` directory at the project root containing `.d.ts` files with `export ` at the top. Next.js also generates a `next-env.d.ts` file — never edit this file manually, as it is regenerated on each build and any changes will be lost. Add your augmentations to a separate file.
Vite
Vite projects use esbuild for transformation and include `isolatedModules: true` in the default tsconfig. Vite also generates a `vite-env.d.ts` file for its own globals. Add module augmentations in a separate `src/types/` directory. The `export ` pattern resolves TS1384 in all standard Vite setups, and keeping augmentations in `.d.ts` files is the safest approach when esbuild transformation is in the chain.
Plain Node.js / Express APIs
Express projects commonly augment `Express.Request` to add user session or auth context. The canonical pattern is a `src/types/express/index.d.ts` file with `export ` at the top followed by the `declare module 'express-serve-static-core'` augmentation. Without `isolatedModules`, this also works as a regular `.ts` file — but using `.d.ts` is the cleaner convention regardless. Always verify the exact module name by checking the Express type definitions, since the augmentation target is `express-serve-static-core`, not `express` itself.
The file must be a module before it can augment one. One `export ` turns a script into a module — and unlocks the entire augmentation system.
Avoiding TS1384 long-term
TS1384 is easy to introduce accidentally, especially when new team members add type augmentations or when you migrate a project to a new bundler. These practices prevent it from returning after you fix it.
Establish a types directory convention
Create a `src/types/` (or `types/`) directory and keep all module augmentations there as `.d.ts` files. Each file should contain one augmentation and start with `export `. Document this convention in your project's `CONTRIBUTING.md` so new contributors know where type declarations belong. A consistent location also makes it easy to audit augmentations when upgrading dependencies.
Use tsc --noEmit in CI
Running `tsc --noEmit` as a CI step catches TS1384 (and every other TypeScript error) before code reaches main. Many projects skip type checking in CI because their bundler does not require it — esbuild and SWC strip types without checking them. Add `tsc --noEmit` as a separate job step so type errors, including TS1384, are caught on every pull request.
Use the Diff Viewer to audit tsconfig changes
Changes to tsconfig.json — adding `isolatedModules`, switching `moduleResolution`, or updating `lib` — can introduce TS1384 in files that previously compiled cleanly. When a tsconfig change lands, use the Diff Viewer to compare the old and new config side-by-side. This makes it immediately visible which options changed and helps trace new TS1384 errors to the specific setting that caused them.
Tip
Diff Viewer
Compare two versions of tsconfig.json, package.json, or any text file side-by-side to find exactly what changed — browser-based, no upload required.
Key takeaways
- TS1384 fires when an `export` modifier appears inside a `declare module` or `declare global` augmentation block — the fix is almost always one line.
- The most common cause: the file has no imports or exports, so TypeScript treats it as a script. Add `export {}` at the top to convert it to a module.
- With `isolatedModules: true` (Vite, Next.js, esbuild projects), move augmentations to `.d.ts` files — they are excluded from transformation and avoid the constraint entirely.
- If TS1384 points to a path inside `node_modules`, the fix is `skipLibCheck: true` in tsconfig.json — you cannot edit dependency declaration files.
- Ambient module definitions (typing untyped libraries) and module augmentations (extending typed libraries) look similar but behave differently — `export` inside the block is only valid in definitions, not augmentations.
- Add `tsc --noEmit` to your CI pipeline to catch TS1384 on every pull request, even if your bundler (esbuild, SWC) does not perform type checking during the build.
- Validate tsconfig.json syntax with the JSON Formatter and Validator after changes — a JSON syntax error silently prevents TypeScript from reading your config.