C.W.K.
Stream
Lesson 05 of 05 · published

Namespaces (and Why ESM Won)

~7 min · modules, namespaces, legacy, history

Level 0Curious
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete
"Namespaces are old TypeScript. ES modules took their job and did it better."

What namespaces are

TypeScript's namespace keyword groups declarations under a single name within a file or across files (with reference tags). namespace Math { export function add() { ... } } creates a Math namespace; you access members with Math.add(). Multiple namespace Math declarations in the same scope merge.

Namespaces predate ES modules. They were TypeScript's original solution to 'how do I organize code without putting everything in the global scope?' before JavaScript had a standard answer.

Why ESM won

ES modules superseded namespaces because:

  • JavaScript standard. ESM is part of the language. Namespaces are TypeScript-only.
  • Bundler-friendly. Tree-shaking works on ESM imports; namespaces ship whole.
  • File-scoped. ESM's natural unit is the file. Namespaces require explicit grouping.
  • Static analysis. Tools (linters, bundlers, IDEs) can analyze imports; namespaces are opaquer.

For new code, never use namespace in .ts source. Use ES modules instead.

Where namespaces still appear

namespace declarations in .d.ts files for legacy libraries: jQuery's types, Lodash's pre-ESM types, large @types/... packages from before the ESM era. You'll read them; you won't write them. Modern library type definitions all use ES modules.

Namespaces are read-only history. Recognize the syntax when you see it in old .d.ts files; don't reach for it in your own code.

Code

Namespaces vs ES modules — the same shape, different mechanisms·typescript
// Legacy: namespace.
namespace MyApp {
  export function init() { /* ... */ }
  export class Config { /* ... */ }

  export namespace Utils {
    export function helper() { /* ... */ }
  }
}

MyApp.init();
MyApp.Utils.helper();

// Modern: ES modules.
// init.ts
export function init() { /* ... */ }

// config.ts
export class Config { /* ... */ }

// utils.ts
export function helper() { /* ... */ }

// app.ts
import { init } from './init';
import { Config } from './config';
import { helper } from './utils';

init();
helper();

External links

Exercise

Take a small namespaced module from a tutorial or legacy codebase. Rewrite it as ES modules — one file per logical unit, named exports, an index.ts barrel. Note what you lost (probably nothing).
Hint
Each export inside a namespace becomes an export in its own file. The namespace wrapper goes away entirely. Imports change from namespace.member to named ES imports.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.