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")); // true

Nested 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.57
AspectNamespaceES Module
Scoping unitsingle global objectone scope per file
File requirementoptional, can share a fileone module per file
Recommended forglobal scripts, ambient .d.tsnew application code
Note: 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`?