TypeScript Namespaces
Namespaces are TypeScript's original way to group related code under a single named container, predating ES modules and still useful for organizing global-scope declarations and legacy code.
Declaring a Namespace
A namespace wraps related interfaces, classes, and functions under one name using the namespace keyword. Members are private to the namespace unless explicitly marked export, which keeps internal helpers out of the public surface.
A validation namespace
namespace Validation {
export interface StringValidator {
isValid(s: string): boolean;
}
export class EmailValidator implements StringValidator {
isValid(s: string): boolean {
return /^\S+@\S+\.\S+$/.test(s);
}
}
// Not exported: invisible outside Validation
const MIN_LENGTH = 3;
}
const validator = new Validation.EmailValidator();
console.log(validator.isValid("a@b.com")); // trueNested Namespaces
Namespaces can be nested using dot notation, which is a convenient way to organize a larger library into sub-groups like Models and Utils without reaching for separate modules.
Nesting with dot notation
namespace App {
export namespace Models {
export interface Product {
id: number;
price: number;
}
}
export namespace Utils {
export function formatPrice(p: Models.Product): string {
return `$${p.price.toFixed(2)}`;
}
}
}
const item: App.Models.Product = { id: 1, price: 19.5 };
console.log(App.Utils.formatPrice(item)); // "$19.50"Namespaces vs ES Modules
- Namespaces compile down to a single global object with nested properties
- ES modules use per-file import/export and are scoped by the module system, not the global object
- Modules are the recommended default for application code written today
- Namespaces still appear heavily in .d.ts files that describe third-party global scripts
- A namespace can be split across multiple files and stitched together with /// <reference> comments
Splitting a namespace across files
// shapes.ts
namespace Shapes {
export interface Circle {
radius: number;
}
}
// area.ts
/// <reference path="shapes.ts" />
namespace Shapes {
export function area(c: Circle): number {
return Math.PI * c.radius ** 2;
}
}
console.log(Shapes.area({ radius: 2 })); // ~12.57Note: For new application code, reach for ES modules first. Namespaces mainly matter today for describing global browser scripts or maintaining legacy codebases that predate widespread module bundlers.
Note: A namespace can be declaration-merged with a function or class of the same name, which is how libraries attach static-like helper members to a callable.
Exercise: TypeScript Namespaces
What is the main purpose of a TypeScript `namespace`?