This TypeScript error occurs when you attempt to export the same symbol name multiple times from a module, or when using re-export statements in module augmentation. The fix involves using direct export declarations instead of re-export syntax, or renaming conflicting exports.
The "Export declaration conflicts with exported declaration" error (TS2484) appears when TypeScript detects that you're trying to export the same symbol name more than once from a module. This typically happens in one of two scenarios: either you have genuinely duplicate exports (exporting the same variable/type/class twice), or you're using re-export syntax (`export { SymbolName }`) in a context where it conflicts with another export. TypeScript enforces that each exported symbol must have a unique name within a module's export namespace. When the compiler encounters two exports with identical names, it cannot determine which one should be exposed to consumers of the module, resulting in this error. The error is particularly common when: 1. **Re-export statements in module augmentation**: Using `export { SymbolName }` syntax instead of inline exports when augmenting modules 2. **Combined declaration files**: Multiple modules exporting the same symbol get merged into a single index.d.ts file 3. **Declaration merging conflicts**: Attempting to merge declarations incorrectly across files 4. **Accidental duplicate exports**: The same variable is exported both inline and in an export block
The most common fix is to replace re-export statements with inline export declarations:
// WRONG - This can cause TS2484 in module augmentation
class MyClass {
// ...
}
export { MyClass };
// CORRECT - Use inline export declaration
export class MyClass {
// ...
}For functions and variables:
// WRONG
function helperFunction() {
return "data";
}
export { helperFunction };
// CORRECT
export function helperFunction() {
return "data";
}This is especially important when augmenting modules or working with declaration files.
Ensure you're not accidentally exporting the same symbol twice:
// WRONG - Duplicate exports
export const API_KEY = "abc123";
// Later in the same file...
export { API_KEY }; // Error: conflicts with earlier export
// CORRECT - Export once only
export const API_KEY = "abc123";Search your file for all occurrences of the symbol name:
# Check for duplicate exports
grep "export.*SymbolName" src/yourfile.tsRemove any duplicate export statements, keeping only one export declaration.
If multiple modules need to export similar functionality, use unique names or namespaces:
// module-a.ts
export interface Config {
timeout: number;
}
// module-b.ts - WRONG: same name causes conflict in combined .d.ts
export interface Config {
retries: number;
}
// module-b.ts - CORRECT: use unique name or namespace
export interface NetworkConfig {
retries: number;
}
// Or use namespaces to avoid conflicts
export namespace ModuleB {
export interface Config {
retries: number;
}
}For re-exports with aliasing:
// index.ts
export { Config as DatabaseConfig } from "./db/config";
export { Config as APIConfig } from "./api/config";When augmenting external modules, ensure you're not creating export conflicts:
// WRONG - Can cause conflicts with export clause
declare module "express" {
interface Request {
user?: User;
}
export { Request }; // This can conflict
}
// CORRECT - Let declaration merging handle it
declare module "express" {
interface Request {
user?: User;
}
}
// CORRECT - Use inline export in augmentation
declare module "my-lib" {
export interface NewFeature {
enabled: boolean;
}
}For module augmentation, prefer declaration merging over explicit export statements.
If the conflict is in node_modules type definitions (e.g., multiple packages defining 'Buffer'), enable skipLibCheck:
// tsconfig.json
{
"compilerOptions": {
"skipLibCheck": true
}
}This tells TypeScript to skip type checking of declaration files, which can resolve conflicts from incompatible @types packages:
# Example: Conflicting Buffer definitions
# from @types/node and another package
npm install @types/node@latest --save-devWarning: This is a workaround, not a proper fix. It may hide legitimate type errors in your dependencies. Use only when dealing with third-party conflicts you cannot control.
Check if you have multiple versions of the same @types package:
# List all installed type packages
npm list | grep @types
# Check for duplicate versions
npm ls @types/node
# Remove duplicates by ensuring compatible versions
npm dedupe
# Or explicitly install a single version
npm install @types/node@^20.0.0 --save-dev --save-exactFor monorepos or complex dependency trees:
# Check which packages are pulling in conflicting types
npm ls @types/packagename
# Use resolutions field in package.json (npm 8.3+)
{
"overrides": {
"@types/node": "^20.0.0"
}
}Clean install after resolving version conflicts:
rm -rf node_modules package-lock.json
npm install### Module Augmentation Best Practices
When augmenting third-party modules, TypeScript's declaration merging has specific rules about exports:
// Good pattern for module augmentation
declare module "express-serve-static-core" {
interface Request {
user?: User;
}
}
// Augmenting with new exports
declare module "my-library" {
export interface ExtendedConfig extends Config {
newOption: boolean;
}
export function newHelperFunction(): void;
}Avoid using export clauses (export { ... }) in module augmentation. Let TypeScript's declaration merging handle the exports automatically.
### Understanding Export Syntax Differences
TypeScript has two main export syntaxes with different behaviors:
Named exports (inline):
export class MyClass {}
export interface MyInterface {}
export function myFunction() {}Re-export syntax:
class MyClass {}
interface MyInterface {}
function myFunction() {}
export { MyClass, MyInterface, myFunction };While both work in regular modules, re-export syntax can cause TS2484 when:
- Used in module augmentation
- Combined with declaration merging
- Bundled into declaration files
Recommendation: Use inline exports by default, especially in .d.ts files and module augmentations.
### Declaration File Bundling Conflicts
When building a library that bundles multiple source files into a single .d.ts file, conflicts can arise:
// src/moduleA.ts
export interface Config {
optionA: boolean;
}
// src/moduleB.ts
export interface Config { // Conflict when bundled
optionB: boolean;
}Solutions:
1. Namespace isolation:
// src/moduleA.ts
export namespace ModuleA {
export interface Config {
optionA: boolean;
}
}2. Unique naming:
// src/moduleA.ts
export interface ModuleAConfig {
optionA: boolean;
}3. Proper module exports in bundler config (rollup-plugin-dts, api-extractor):
// Ensure each module maintains its own scope
export default {
input: {
moduleA: 'src/moduleA.ts',
moduleB: 'src/moduleB.ts'
},
output: {
dir: 'dist',
format: 'es'
}
}### Debugging Export Conflicts
Find all exports in your codebase:
# Search for all export statements
grep -rn "export.*SymbolName" src/
# Find duplicate declarations
grep -rn "^export (class|interface|type|const|function) SymbolName" src/Generate declaration files to see bundled output:
npx tsc --declaration --emitDeclarationOnly
# Check generated .d.ts files for conflicts
cat dist/index.d.ts | grep "export.*SymbolName"### Type vs Value Export Conflicts
TypeScript distinguishes between type exports and value exports. Conflicts occur when you export both:
// This is fine - same name, same entity
export interface User {
name: string;
}
// This causes conflict - User is both type and value
export const User = {
create: () => ({ name: "" })
};
// Solution: Use different names
export interface UserType {
name: string;
}
export const UserFactory = {
create: (): UserType => ({ name: "" })
};
// Or use namespace for grouping
export namespace User {
export interface Type {
name: string;
}
export const create = (): Type => ({ name: "" });
}### Global Augmentation Conflicts
When augmenting global scope, ensure no conflicts with existing globals:
// global.d.ts
declare global {
interface Window {
customProperty: string;
}
// WRONG - conflicts with built-in Buffer
interface Buffer {
customMethod(): void;
}
// CORRECT - extend existing Buffer instead
// (requires @types/node)
namespace NodeJS {
interface Buffer {
customMethod(): void;
}
}
}
export {}; // Make this a moduleFunction expression requires a return type
Function expression requires a return type
Value of type 'string | undefined' is not iterable
How to fix "Value is not iterable" in TypeScript
Type 'undefined' is not assignable to type 'string'
How to fix "Type undefined is not assignable to type string" in TypeScript
Type narrowing from typeof check produces 'never'
How to fix "Type narrowing produces never" in TypeScript
Type parameter 'T' has conflicting constraints
How to fix "Type parameter has conflicting constraints" in TypeScript