Menu

TypeScriptのnamespace(名前空間)とは?使いどころを解説

TypeScriptの namespace は、値と型をひとつの名前の下にまとめ、普通のオブジェクトにコンパイルされます。構文、namespace どうしや関数・クラスとのマージ、ES モジュールに置き換えられた理由、宣言ファイルとグローバル拡張など今も namespace に出会う場面を解説します。

このページのコードはエディタで実行できます - 編集してすぐに結果を確認できます。

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 は消去されるので問題なく動きます。

Coddy programming languages illustration

Coddyでコードを学ぼう

始める