This error occurs when you try to use a namespace as a type annotation instead of as a value. Namespaces are organizational containers, not type definitions. The fix involves using 'typeof' keyword or refactoring to use interfaces/types directly.
In TypeScript, namespaces and types serve different purposes. A namespace is a organizational container that groups related code, while a type or interface defines the structure of data. TypeScript distinguishes between the namespace itself (a value) and the types contained within it. When you try to use a namespace name directly as a type annotation, TypeScript can't determine what you mean because a namespace is not a typeβit's a collection of values and types. This error typically occurs in three scenarios: 1. Using the namespace name directly as a type instead of accessing a type within it 2. Mixing module declarations with imports inconsistently 3. Importing namespace modules and trying to use the namespace as a type The compiler error code is TS2709.
First, understand the structure of the namespace you're using. If you have a namespace with exported members, you need to access the specific type, not the namespace itself.
Common namespace structure:
namespace Shapes {
export interface Circle {
radius: number;
}
export interface Square {
side: number;
}
}When using this, you cannot do:
let shape: Shapes; // ERROR: Cannot use namespace 'Shapes' as a typeInstead, access the specific type within it:
let circle: Shapes.Circle; // CORRECTIf you want to capture the type of a namespace itself (not an exported type within it), use the 'typeof' operator:
import * as Vector3 from './Vector3'
// Instead of:
type Vec = Vector3; // ERROR
// Use typeof:
type Vec = typeof Vector3;This creates a type that represents the shape of the namespace object itself, including all its exported members.
Instead of relying on namespaces for type grouping, use ES6 modules with explicit exports:
Before (namespace approach - problematic):
namespace Car {
export interface Props {
color: string;
wheels: number;
}
}
// This fails:
type CarType = Car; // ERROR: Cannot use namespace as a typeAfter (module approach - recommended):
// car.ts
export interface CarProps {
color: string;
wheels: number;
}
// usage.ts
import type { CarProps } from './car'
type Car = CarProps; // CORRECTIf you have a 'declare module' statement conflicting with an import, remove the unnecessary declaration:
Problematic code:
declare module 'mylib'; // Declares a namespace
import { MyInterface } from 'mylib'; // Imports from same module
type Result = MyInterface; // This can cause confusionSolution:
Remove the 'declare module' line if you're already importing from the module. Use one approach or the other, not both:
// Keep only this:
import { MyInterface } from 'mylib';
type Result = MyInterface; // Now works correctlyIf you must work with existing namespaces, access the types within them using dot notation:
namespace Database {
export interface Connection {
host: string;
port: number;
}
export class ConnectionPool {
// implementation
}
}
// Correct ways to use it:
let conn: Database.Connection; // Access the interface
let pool = new Database.ConnectionPool(); // Access the class
// Incorrect:
let x: Database; // ERROR: Cannot use namespace as a type### Why Namespaces Are Problematic
TypeScript namespaces are an older pattern for code organization. They predate ES6 modules and were useful when modules weren't standardized. However, modern TypeScript and JavaScript use ES6 module syntax, which provides better clarity.
Mixing namespaces with modules creates ambiguity:
// Namespace declares a value container
namespace Utils {
export const helper = () => {}
}
// But Utils is also a value, not just a type
// So: type X = Utils; is ambiguousThe recommended pattern today is:
// Export types and values separately
export interface Config {
// type definition
}
export const createConfig = () => ({}) // value### Barrel Exports
If you organize code with barrel exports (index.ts files), you can avoid namespace issues:
// api/index.ts (barrel file)
export * from './types';
export * from './client';
export * from './middleware';
// Usage:
import type { Request } from './api'; // Clear type import
import { createClient } from './api'; // Clear value import### TypeScript Compiler Strict Mode
The --strict compiler flag makes namespace issues more apparent. If you see this error with strict mode enabled, it's almost always because of ambiguous namespace usage.
### Type-Only Imports (TypeScript 3.8+)
Use type-only imports to be explicit:
// Instead of:
import { Something } from './module';
// Use type-only imports for types:
import type { Something } from './module';
// This prevents namespace ambiguity issuesFunction 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