JavaScript如果要替名稱加上命名空間,不外乎就是利用物件或是模組來達成。但在TypeScript中,有namespace關鍵字可以讓我們快速地新增命名空間來用。



namespace關鍵字的用法很簡單,如下:

namespace 命名空間的名稱(可以用句號連接多個名稱) {
    程式敘述區塊
}

若要將namespace程式區塊中的名稱暴露出來,與模組一樣,是在varletconstfunctionclassinterfaceenumtype關鍵字前加上export關鍵字。當然,namespace程式區塊中也還可以再使用namespace關鍵字來建立程式敘述區塊,且namespace關鍵字前也是可以加上export關鍵字來將其暴露的。

例如:

namespace org.magiclen {
    export namespace A {
        export function f() {
            console.log("A");
        }
    }

    export namespace B {
        export function f() {
            console.log("B");
        }
    }

    export namespace AB {
        export function f() {
            A.f();
            B.f();
        }
    }
}

org.magiclen.A.f();
org.magiclen.B.f();
org.magiclen.AB.f();

以上程式的輸出結果如下:

A
B
A
B

擴充命名空間

我們可以建立出相同名稱的namespace程式敘述區塊,替已存在的命名空間擴充原本不存在的名稱。

例如:

namespace org.magiclen {
    export namespace A {
        export function f() {
            console.log("A");
        }
    }
    export namespace A {
        export function f2() {
            console.log("A");
        }
    }

    export namespace B {
        export function f() {
            console.log("B");
        }
    }

    export namespace AB {
        export function f() {
            A.f();
            B.f();
        }
    }
}

namespace org.magiclen.A {
    export function ff() {
        console.log("AA");
    }
}

namespace org.magiclen {
    export function f() {
        console.log("Hello!");
    }
}

org.magiclen.A.f();
org.magiclen.B.f();
org.magiclen.AB.f();

org.magiclen.A.ff();
org.magiclen.f();

命名空間除了可以拿來擴充同名的命名空間之外,也可以拿來擴充同名的變數、常數、函數、類別、介面、列舉和型別別名下的名稱。有點詭異,但十分方便。例如:

type T = number | string;

namespace T {
    export const n = 1;
}

console.log(T.n);

function f() {

}

namespace f {
    export const s = "2";
}

console.log(f.s);

命名空間的網頁應用

實際編譯以下TypeScript程式:

namespace A {
    export function f() {
        console.log("A");
    }
}

會得到這樣的JavaScript程式:

var A;
(function (A) {
    function f() {
        console.log('A');
    }
    A.f = f;
})(A || (A = {}));

A命名空間實際上在JavaScript中會是一個變數A。也就是說,利用這樣的性質,我們可以很容易地在網頁瀏覽器上透過命名空間產生的變數,來存取這個變數下的名稱。

舉例來說,現在有以下兩個檔案:

namespace A {
    export function f() {
        console.log("A");
    }
}
namespace B {
    export function f() {
        console.log("B");
    }
}

以上兩個檔案雖然都不是TypeScript模組(因為最外層並沒有使用export關鍵字),但是它們的f函數在網頁中並不會有衝突,可以透過全域變數AB來呼叫它們。如下:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8"/>
        <script src="a.js"></script>
        <script src="b.js"></script>
    </head>
    <body>
        <script>
            A.f();
            B.f();
        </script>
    </body>
</html>

總結

至此我們已經把TypeScript能夠宣告的名稱類型(變數、常數、函數、類別、介面、列舉、型別別名、模組、命名空間)都學全了。只是有些名稱可以當作型別,有些名稱可以當作值,有些名稱又可以當作命名空間來用,實在很混亂。在此用以下表格做個歸納:

名稱類型 命名空間 型別 例子
var
var name = "MagicLen";

console.log(name);
let
let name = "MagicLen";

console.log(name);
const
const name = "MagicLen";

console.log(name);
function
function f() {}

console.log(f);
class
class C {}

console.log(C);

let o: C = new C();
interface
interface I {}

class C implements I {}

let o: I = new C();
enum
enum E { // it can not be a const enum
    V
}

console.log(E);

let o: E = E.V;
type
interface I {}

let o: I;
import
import * as fs from "node:fs";

console.log(fs);
namespace
namespace NS {
    export function f() {}
}

NS.f();

在下一個章節中,我們要來學習TypeScript的裝飾器(Decorator)。

下一章:裝飾器(Decorator)