namespace

Groups related declarations under one name, emitting an IIFE when it contains values.

Signature
namespace Name {
  const privateHelper = 1;        // not exported: invisible outside
  export interface SomeType { }   // exported: reachable as Name.SomeType
  export function fn(): void { }
  export namespace Nested { }
}

// Aliasing a nested namespace:
import Alias = Name.Nested;
  • Added in TypeScript 1.5

Parameters

ParameterTypeDescription
$Nameidentifier | dotted nameThe namespace name. A dotted name such as `A.B.C` declares nested namespaces in one step. Same-named namespaces in the same scope merge.
$exportoptionalmodifier= not exportedRequired on any member that should be visible outside the namespace. Non-exported members stay private to the block they were declared in, even across merged declarations of the same namespace.
$bodydeclarationsAny mix of types, interfaces, classes, functions, variables and nested namespaces. If it contains only types, the whole namespace is erased; if it contains values, an IIFE is emitted.

Errors

Errors at compile time when code in one declaration of a merged namespace references a non-exported member declared in a different block, and when a namespace containing values is used under erasableSyntaxOnly.

Changelog

  • PHP 1.5The keyword `namespace` replaced `module X { }` for internal modules. `module` still parses but `namespace` is the preferred spelling.
  • PHP 5.8erasableSyntaxOnly rejects namespaces that contain runtime code. Type-only namespaces remain legal because they are fully erased.

A namespace wraps declarations in a named object. If it holds only types it disappears; if it holds values, the compiler emits an IIFE that assigns each exported member onto an object — the same pattern that predates ES modules.

When to reach for it

For new application code, do not. ES modules give you the same encapsulation with real dependency tracking, tree shaking and tooling support. Namespaces remain the right tool in exactly two places:

  • Declaration files. declare namespace is how you describe a global library that exposes dotted access, such as jQuery.fn or a UMD global.
  • Merging a namespace with a value. A namespace can merge with a function, class or enum to attach static members or an inner type — buildLabel.prefix, or Album.AlbumLabel. There is no module equivalent.

What trips people up

export is not optional. Anything without it is invisible outside the braces, and — the subtler half — invisible to other declarations of the same namespace, so splitting a namespace across files breaks any shared private helper. Namespaces also do not compose with modules: once a file has a top-level import or export, its namespaces are scoped to that module and are no longer global.

Example

Example · typescript
namespace Validation {
  const zipRegexp = /^[0-9]{5}$/;   // private: no export keyword

  export interface StringValidator {
    isAcceptable(s: string): boolean;
  }

  export class ZipCodeValidator implements StringValidator {
    isAcceptable(s: string) {
      return s.length === 5 && zipRegexp.test(s);
    }
  }
}

const v: Validation.StringValidator = new Validation.ZipCodeValidator();
console.log(v.isAcceptable("90210"), v.isAcceptable("1234"));
// true false

When to use it

  • Describe a global script library with dotted access in a hand-written .d.ts file.
  • Attach typed static configuration properties onto an exported function.
  • Add helper functions to an existing enum by merging a namespace with it.
  • Declare an inner class or type reachable as a property of an outer class.

Frequently asked questions

Should I use namespaces or modules in TypeScript?

Use ES modules for application code — they provide real encapsulation, explicit dependencies and tree shaking, none of which namespaces offer. Namespaces are still appropriate in declaration files describing global libraries, and for merging static members onto a function, class or enum.

Why is my namespace member undefined at runtime?

Almost always because the member is missing the `export` keyword, so it never gets assigned onto the emitted namespace object. The other common cause is a build tool that strips types per file — esbuild, sucrase and similar transpilers erase namespace blocks instead of emitting the IIFE.

What replaced the module keyword in TypeScript?

`namespace` replaced `module X { }` for internal modules in TypeScript 1.5, so that `module` could align with ECMAScript's meaning. The old spelling still parses, but `namespace` is preferred; `declare module "name"` remains correct for describing an external module.

More examples

Merging a namespace with a function to add statics

The namespace merges into the function declaration, so buildLabel is callable and also carries typed properties. Mutating prefix changes the next call's output, confirming these are ordinary properties on the function object rather than anything type-level. This is the pattern used to type jQuery-style APIs, and it is the main reason namespaces still appear in modern declaration files.

Example · typescript
function buildLabel(name: string): string {
  return buildLabel.prefix + name + buildLabel.suffix;
}

namespace buildLabel {
  export let prefix = "Hello, ";
  export let suffix = "";
}

console.log(buildLabel("Sam Smith"));
// Hello, Sam Smith

buildLabel.prefix = "Goodbye, ";
buildLabel.suffix = "!";
console.log(buildLabel("Sam Smith"));
// Goodbye, Sam Smith!

The private-member trap when a namespace is split

The two blocks merge into one namespace, but haveMuscles was never exported, so it is only visible inside the block that declared it. The second block has to call the exported function instead. This bites hardest when a namespace is split across files: the shared helper you assumed was module-private is simply unreachable from the other half.

Example · typescript
namespace Animal {
  let haveMuscles = true;               // private to THIS block

  export function animalsHaveMuscles(): boolean {
    return haveMuscles;                 // fine
  }
}

namespace Animal {
  export function doAnimalsHaveMuscles(): boolean {
    // return haveMuscles;
    // Error: Cannot find name 'haveMuscles'.
    return animalsHaveMuscles();        // must go through the exported API
  }
}

console.log(Animal.animalsHaveMuscles(), Animal.doAnimalsHaveMuscles());
// true true

Discussion

  • Be the first to comment on this lesson.