namespace は、関数、定数、型をひとつの名前の下にまとめる名前付きのブロックです。export を付けたメンバーには Name.member でアクセスでき、それ以外はブロックの中だけのものになります。実行時の namespace は普通のオブジェクトです。
namespace のコンパイル結果
namespace は、コードを生成するTypeScriptの数少ない機能のひとつです。上のブロックは、オブジェクトの中身を埋める関数になります:
var Geometry;
(function (Geometry) {
const TAU = Math.PI * 2;
function circumference(radius) {
return TAU * radius;
}
Geometry.circumference = circumference;
function area(radius) {
return Math.PI * radius ** 2;
}
Geometry.area = area;
})(Geometry || (Geometry = {}));
これは、モジュールが登場する前のJavaScriptのコードが、すべての名前をグローバルスコープに置かないために使っていたパターンです。TAU は関数のローカル変数なので、ほかのコードからは見えません。読もうとするとコンパイルエラーになります:
コンパイラーは index.ts(10,17): error TS2339: Property 'rate' does not exist on type 'typeof Tax'. と報告します。const rate の前に export を付ければ、両方の行が実行できます。
入れ子、マージ、エイリアス
namespace は入れ子にでき、同じ名前の2つのブロックはひとつにマージされます。出力の Geometry || (Geometry = {}) がそれを可能にしています。2つ目のブロックは既存のオブジェクトにメンバーを追加するのです。import X = A.B で短いエイリアスを作れます。
namespace A.B.C { } は、3つの入れ子のブロックの省略形です。古い書き方の module Shop { } は同じ意味ですが、今ではエラー TS1540 A 'namespace' declaration should not be declared using the 'module' keyword. Please use the 'namespace' keyword instead. で拒否されます。
関数やクラスとのマージ
namespace は関数、クラス、enum と同じ名前を持ち、それにメンバーを追加できます。プロパティも持つ関数や、ヘルパーを付け加えたクラスを表すには、今でもこれがいちばんすっきりした方法です。
クラスなら、static メソッドでも同じことができ、しかも普通のJavaScriptです。関数なら、namespace を使わずに宣言の直後で format.prefix = "$" と代入することもできます。TypeScriptはそのように代入されたプロパティを追跡します。
namespace とモジュール
ES モジュールが登場する前は、大きなTypeScriptのプログラムはグローバルな namespace を共有する多数のスクリプトファイルで、/// <reference path="..." /> でつなぎ合わせ、outFile でひとつのファイルにコンパイルしていました。モジュールがそれを置き換えました。各ファイルが独自のスコープを持ち、依存関係は明示的な import になり、バンドラーは使われていないエクスポートを取り除けます。TypeScript 7 では outFile が削除された(エラー TS5102)ので、複数ファイルにまたがる namespace の構成はもうビルドの選択肢にありません。
| namespace | モジュール | |
|---|---|---|
| 単位 | ファイルの中の名前付きのブロック | ファイルそのもの |
| スコープ | モジュールの中でなければグローバル | 常に独自 |
| 依存関係 | 暗黙的、読み込み順で決まる | 明示的な import |
| 出力 | 関数が組み立てるオブジェクト | import/export または require |
| 未使用コードの削除 | バンドラーはすべてのメンバーを残す | バンドラーは未使用のエクスポートを削除できる |
| Node の型除去で動く | 型だけを含む場合のみ | 動く |
モジュールの中ですべてを namespace で包むと、何の利点もなく名前の階層がもう一段増えます。import する側は Utils.Utils.format と書くことになります。関数を直接エクスポートし、接頭辞がほしければ import する側が import * as Utils from "./utils.js" を選べるようにしましょう。
今も namespace に出会う場面
宣言ファイルでは、グローバルなオブジェクトをひとつ公開するライブラリを表したり、型をまとめたりするために使われます:
// jquery-like.d.ts: a global function that also has properties
declare function $(selector: string): unknown;
declare namespace $ {
const version: string;
function ajax(url: string): Promise<unknown>;
}
型パッケージは、拡張できる型を公開するために使います。@types/node は namespace NodeJS を宣言しており、任意のモジュールからその ProcessEnv インターフェースに追加するには declare global が必要です:
// env.d.ts
export {};
declare global {
namespace NodeJS {
interface ProcessEnv {
API_URL: string; // process.env.API_URL is now string, not string | undefined
}
}
}
マージされた namespace の中のインターフェースもマージされます。この拡張が働くのはそのためです。
namespace と型除去
Node 24 は型の構文を削除することで .ts ファイルを実行します。値を持つ namespace は削除できず、上で見たオブジェクトにコンパイルする必要があるので、node app.ts は次のエラーで止まります:
SyntaxError [ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX]: TypeScript namespace declaration is not supported in strip-only mode
完全に消えるので問題ない namespace が2種類あります。declare namespace と、メンバーがすべて型かインターフェースの namespace です。コンパイラーオプション erasableSyntaxOnly: true はそれ以外をコンパイル時にエラー TS1294 This syntax is not allowed when 'erasableSyntaxOnly' is enabled. として報告します。型除去で動かすプロジェクトはこれで namespace を締め出しています。node --experimental-transform-types は、実験的機能の警告付きで namespace をコンパイルします。
よくある質問
TypeScriptでは namespace とモジュールのどちらを使うべきですか?
新しいコードではモジュール(import と export)を使います。どのファイルもすでに独自のスコープを持ち、バンドラーも Node もモジュールを理解し、使われていないエクスポートを取り除けます。namespace が今も役立つのは、宣言ファイル、グローバル拡張、同じ名前の関数やクラスに型やヘルパーを付け加える場面です。
TypeScriptの namespace は何にコンパイルされますか?
即時実行関数で中身を埋めるオブジェクトです: var Geometry; (function (Geometry) { Geometry.circle = circle; })(Geometry || (Geometry = {}));。エクスポートしたメンバーはそのオブジェクトのプロパティになり、export のないメンバーは関数のローカルのままです。
TypeScriptの namespace と module の違いは何ですか?
モジュールは、トップレベルに import か export があるファイルです。namespace はファイルの中の名前付きのブロックです。昔のTypeScriptは namespace を「内部モジュール」と呼び、module Foo {} と書けましたが、今ではその書き方はエラー TS1540 で、namespace Foo {} しか受け付けません。
namespace を使うTypeScriptのファイルを Node で実行できますか?
デフォルトの型除去ではできません。値を含む namespace は ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX になります。型除去は型を削除するだけで、namespace にはコードの生成が必要だからです。型だけを含む namespace や declare namespace は消去されるので問題なく動きます。