This TypeScript error occurs when you attempt to export a member directly from within a namespace declaration, usually when trying to re-export types or values from external modules. The fix involves using proper export syntax outside the namespace or restructuring your code to use ES6 modules instead of namespaces.
The error "Cannot export member 'X' in namespace" occurs when TypeScript encounters export statements inside namespace declarations that it cannot handle. This typically happens in these scenarios: 1. You're trying to re-export a member from an external module from within a namespace 2. You're mixing namespace declarations with ES6 module export syntax 3. You're attempting to export namespace members that conflict with module exports 4. You're using export assignments within a namespace (which is not allowed) TypeScript namespaces are a legacy feature from before ES6 modules existed. They group related code under a single name but don't follow the same export rules as ES6 modules. When TypeScript encounters conflicting or unsupported export patterns in namespaces, it raises this error. The error is TypeScript's way of preventing ambiguous export patterns that could cause confusion about what's actually being exported from your module.
The most direct fix is to define your exports at the module level, outside of the namespace:
Before (causes error):
namespace MyNamespace {
export type User = {
id: string;
name: string;
};
export const getUserName = (user: User) => user.name;
}
export { MyNamespace };After (fixed):
namespace MyNamespace {
export type User = {
id: string;
name: string;
};
export const getUserName = (user: User) => user.name;
}
// Export the namespace itself, not individual members
export namespace MyNamespace {}Alternatively, export members directly at module level:
export type User = {
id: string;
name: string;
};
export const getUserName = (user: User) => user.name;This approach clarifies what's being exported and aligns with ES6 module semantics.
The most modern solution is to replace namespaces with ES6 modules, which are the standard in TypeScript and JavaScript:
Before (namespace-based):
// users.ts
namespace Users {
export type User = {
id: string;
name: string;
};
export const getUserName = (user: User) => user.name;
}
export { Users };After (ES6 modules):
// users.ts
export type User = {
id: string;
name: string;
};
export const getUserName = (user: User) => user.name;Consumer (before):
// app.ts
import { Users } from "./users";
const user: Users.User = { id: "1", name: "Alice" };Consumer (after):
// app.ts
import type { User } from "./users";
import { getUserName } from "./users";
const user: User = { id: "1", name: "Alice" };ES6 modules are:
- The standard across JavaScript ecosystem
- Better for tree-shaking and bundler optimization
- More compatible with modern tooling
- Easier to understand and maintain
- The recommended approach for new TypeScript code
When re-exporting members from external modules, use the correct export syntax:
Wrong (causes error):
// api-types.ts
import { User as ExternalUser } from "external-lib";
namespace API {
export type User = ExternalUser; // Error!
}
export { API };Correct:
// api-types.ts
export { User as APIUser } from "external-lib";
// Or if you want to wrap it:
import type { User as ExternalUser } from "external-lib";
export type User = ExternalUser;For re-exporting multiple items:
// index.ts
export * from "./users";
export * from "./products";
export * from "./orders";For selective re-exports:
// index.ts
export { User, createUser } from "./users";
export { Product, getProduct } from "./products";
export type { Order } from "./orders";Proper export syntax ensures TypeScript can correctly track what's being exported and to whom.
Never use export assignment (export =) inside a namespace - it's not allowed:
Wrong (causes error):
namespace MyNamespace {
const value = "test";
export = value; // ERROR: TS1363
}Correct (use export statements instead):
namespace MyNamespace {
export const value = "test";
}
export { MyNamespace };If you need default export, use at module level:
// utils.ts
const utilities = {
value: "test",
getName: () => "Utils",
};
export default utilities;CommonJS-style export:
If you must use CommonJS style (which is legacy), do it outside namespace:
namespace MyNamespace {
export const value = "test";
}
export = MyNamespace;Export assignments should only appear at the module level, not inside namespace declarations.
Ensure that member names aren't conflicting with existing exports:
Problem: Duplicate exports:
namespace API {
export type Response = { status: number };
}
export type Response = string; // Conflict!
export { API };Solution: Use explicit naming:
namespace API {
export type APIResponse = { status: number };
}
export type Response = string;
export { API };
// Consumers can now distinguish:
// type APIResponse = API.APIResponse;
// type MyResponse = Response;Or use separate files:
// api/types.ts
export type Response = { status: number };
// app/types.ts
export type Response = string;Check for circular dependencies:
# Use TypeScript compiler to check dependencies
tsc --listFilesOnly
# Look for files importing each otherClear naming and proper file organization prevent conflicts.
Ensure your tsconfig.json supports proper module syntax:
Check your tsconfig.json:
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "node",
"declaration": true,
"strict": true,
"esModuleInterop": true
}
}Key settings for avoiding namespace issues:
{
"compilerOptions": {
// Use modern module syntax
"module": "ESNext",
// Enable proper Node.js resolution
"moduleResolution": "node",
// Allow importing CommonJS modules
"esModuleInterop": true,
// Generate .d.ts files for libraries
"declaration": true,
// Strict null checks catch more issues
"strict": true
}
}For library authors:
{
"compilerOptions": {
"outDir": "./dist",
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"module": "ESNext"
}
}Proper configuration helps TypeScript correctly understand your export patterns and report errors accurately.
If you're working with legacy code using namespaces, refactor systematically:
Step 1: Identify all namespace exports
// Find all namespace declarations
grep -r "namespace " src/ --include="*.ts"
grep -r "export namespace" src/ --include="*.ts"Step 2: Convert one namespace at a time to modules
// Before: namespace with nested members
namespace Utils {
namespace String {
export const capitalize = (str: string) => str.charAt(0).toUpperCase() + str.slice(1);
}
namespace Array {
export const unique = <T>(arr: T[]) => [...new Set(arr)];
}
}// After: proper file structure
// utils/string.ts
export const capitalize = (str: string) => str.charAt(0).toUpperCase() + str.slice(1);
// utils/array.ts
export const unique = <T>(arr: T[]) => [...new Set(arr)];
// utils/index.ts
export * as String from "./string";
export * as Array from "./array";Step 3: Update imports in consuming code
// Before
import { Utils } from "./utils";
Utils.String.capitalize("hello");
// After
import { String } from "./utils";
String.capitalize("hello");Step 4: Run tests and verify all exports work
npm test
npm run buildSystematic refactoring prevents breaking changes while modernizing your codebase.
Namespaces vs Modules: A Historical Context
TypeScript namespaces were created before ES6 modules existed, to provide a way to organize code and avoid global namespace pollution. However, ES6 modules (the standard since 2015) provide a superior solution:
- Namespaces: Use dot notation (namespace.member), require import statements at the file level, are less compatible with build tools
- Modules: Use declarative syntax, allow selective imports, work seamlessly with tree-shaking and bundlers
Why You Shouldn't Use Namespaces (Unless Supporting Legacy Code)
The TypeScript handbook itself recommends against namespaces for new code. They:
1. Add cognitive overhead (extra nesting levels)
2. Complicate bundler optimization
3. Create ambiguity about what's exported from a file
4. Are incompatible with modern build tools and package standards
5. Make circular dependency detection harder
Export Patterns and Their Rules
Named exports (recommended):
// At module level only
export const feature1 = () => {};
export type Config = { /* ... */ };
export class Service {}Namespace re-export:
// Define at module level, not inside namespace
namespace API {
export const endpoint = "/api";
}
export { API };Barrel exports (re-exporting from one file):
// index.ts
export * from "./users";
export * from "./products";
export { default as logger } from "./logger";Default + named exports:
export default function createApp() {}
export const config = { debug: true };Export assignment (CommonJS-style, legacy):
// Only at module level
export = MyClass;Common Mistakes with Namespaces
1. Attempting to export from within namespace:
namespace Foo {
export const x = 1;
}
export { Foo }; // OK - exports the namespace
export Foo.x; // ERROR - can't export member of namespace2. Mixing export patterns:
export namespace API {} // Fine
export const apiKey = "secret"; // Fine
export type Request = {}; // Fine
// But avoid:
namespace API {
export = someValue; // ERROR
}3. Expecting namespace members to be top-level exports:
namespace Utils {
export const helper = () => {};
}
// This doesn't work:
import { helper } from "./utils"; // ERROR
// Must be:
import { Utils } from "./utils";
Utils.helper();
// Or convert to modules:
// utils.ts has: export const helper = () => {};
import { helper } from "./utils"; // OKMigration Strategy for Large Codebases
If you have a large namespace-based codebase:
1. Phase 1: Enable "skipLibCheck": true and "allowSyntheticDefaultImports": true in tsconfig to reduce errors
2. Phase 2: Identify critical namespaces and convert them first
3. Phase 3: Update imports gradually, file by file
4. Phase 4: Run extensive tests to ensure no functionality breaks
5. Phase 5: Remove namespace declarations entirely
Use TypeScript's language server to assist with automated refactoring:
- Use "Rename Symbol" to update all references
- Use "Organize Imports" to clean up after refactoring
- Use "Go to References" to find all usages
Debugging Namespace Export Issues
When stuck, use these debugging techniques:
// Check what TypeScript thinks is exported
const check: typeof MyNamespace = {};
// Look at generated .d.ts files
tsc --declaration --declarationDir ./dist
// Enable verbose logging
tsc --diagnostics --prettyReview the generated type definitions to see what TypeScript actually exported - this often reveals the issue.
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