This TypeScript error occurs when you have multiple namespace declarations with conflicting structures. It typically happens when merging namespaces from different files or when type definitions have incompatible members. The fix involves ensuring consistent namespace structure across all declarations.
The error "Namespace 'X' already has a declaration with different structure" occurs when TypeScript encounters multiple declarations of the same namespace with incompatible internal structures. TypeScript supports declaration merging for namespaces, but all merged declarations must have compatible members. This error typically happens in these scenarios: 1. Multiple .d.ts files declare the same namespace with different members 2. A namespace is declared in one file with certain exports, and another file tries to add incompatible members 3. Third-party type definitions conflict with your own namespace declarations 4. Module augmentation attempts to add members that conflict with existing namespace structure TypeScript performs structural compatibility checks during declaration merging. If two namespace declarations have members with the same name but different types, or if one declaration has members that are incompatible with another, TypeScript raises this error to prevent type safety violations.
First, locate all declarations of the problematic namespace. Search your codebase for the namespace name mentioned in the error:
# Search for namespace declarations
grep -r "namespace\s*NamespaceName" src/
grep -r "declare\s*namespace\s*NamespaceName" ./Also check:
- All .d.ts files in node_modules/@types/
- Global type definitions in src/types/ or similar directories
- Ambient declarations using declare global
- Module augmentations using declare module
Examine each namespace declaration and compare their members. Look for:
1. Same member name, different types:
// File 1
namespace MyNamespace {
export const value: string;
}
// File 2 - ERROR: Different type
namespace MyNamespace {
export const value: number; // Conflict!
}2. Missing or extra members:
// File 1
namespace MyNamespace {
export function doSomething(): void;
}
// File 2
namespace MyNamespace {
// Missing doSomething() - OK for merging
export function doAnotherThing(): void;
}3. Incompatible member kinds (function vs variable):
// File 1
namespace MyNamespace {
export const config: Config; // Variable
}
// File 2 - ERROR: Different kind
namespace MyNamespace {
export function config(): Config; // Function - Conflict!
}If the conflict is due to type differences, consider using union types or making types more general:
// Instead of conflicting specific types:
namespace MyNamespace {
export const value: string; // File 1
export const value: number; // File 2 - ERROR!
}
// Use a union type in a single declaration:
namespace MyNamespace {
export const value: string | number;
}
// Or use conditional exports based on environment:
namespace MyNamespace {
export const value: string;
}
// In another file for specific environment:
declare module './my-namespace' {
namespace MyNamespace {
// Only augment if not already defined
export const value?: number;
}
}For functions, ensure consistent parameter and return types.
Choose one of these strategies:
Option 1: Consolidate into single declaration
Move all namespace members into one primary .d.ts file and remove duplicates.
Option 2: Use module augmentation properly
// Base declaration
declare namespace MyLib {
interface Config {
timeout: number;
}
}
// Correct augmentation
declare namespace MyLib {
interface Config {
retries?: number; // Adding optional property - OK
}
}Option 3: Use different namespaces
If declarations are truly incompatible, use different namespace names:
namespace MyNamespaceV1 { /* ... */ }
namespace MyNamespaceV2 { /* ... */ }Third-party type definitions often cause conflicts:
1. Check for duplicate @types packages:
npm list @types/package-name
# Or for yarn:
yarn why @types/package-name2. Remove duplicate type definitions:
npm uninstall @types/conflicting-package
# Or specify exact version:
npm install @types/[email protected]3. Use TypeScript's typesVersions for compatibility:
In package.json:
{
"typesVersions": {
">=4.0": { "*": ["ts4.0/*"] }
}
}4. Consider using skipLibCheck temporarily:
In tsconfig.json (not recommended long-term):
{
"compilerOptions": {
"skipLibCheck": true
}
}After making changes:
1. Clear TypeScript cache:
# Delete output directories
rm -rf dist/ build/ node_modules/.cache/
# Or use TypeScript's --clean flag
npx tsc --clean2. Run type checking:
npx tsc --noEmit3. Test with your build process:
npm run build
# or
yarn build4. Verify in IDE:
Restart your TypeScript language server (usually Ctrl+Shift+P → "TypeScript: Restart TS Server" in VS Code).
If the error persists, use tsc --traceResolution to see how TypeScript resolves the conflicting declarations.
## Declaration Merging Rules
TypeScript has specific rules for namespace merging:
1. Non-exported members are not visible across declarations
2. Later declarations augment earlier ones - order matters in some cases
3. Namespace merging creates a union of all exported declarations
4. Conflicts occur when:
- Same exported name has incompatible types
- Function overloads have incompatible signatures
- Class and interface merges have conflicting members
## Common Pitfalls
1. Global augmentation conflicts: declare global can conflict with module-scoped declarations
2. Triple-slash directives: /// <reference path="..." /> can cause duplicate declarations
3. Path mapping issues: tsconfig.json paths can resolve to different files than expected
4. Symlinked packages: In monorepos, symlinks can cause TypeScript to see multiple copies
## Debugging Tools
- tsc --diagnostics: Shows detailed compilation statistics
- tsc --listFiles: Lists all files included in compilation
- tsc --explainFiles: Explains why each file is included
- VS Code's "Go to Definition" on the namespace to see all declarations
## When to Use Alternative Approaches
1. Modules instead of namespaces: Consider using ES modules (import/export) which have clearer boundaries
2. Type aliases and interfaces: For type-only concerns, interfaces merge more gracefully
3. Conditional types: Use type utilities to handle different scenarios
Function 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