1、命名空間
ts 中的 “命名空間” 就是之前的 “內部模塊”,任何使用 module 關鍵字來聲明一個內部模塊的地方都應該使用 namespace 關鍵字來替換
// ts 中的“內部模塊” (廢棄) module X { } // ts 中的“命名空間” (推薦) namespace X { }
(1)、export 關鍵字
使用 export 關鍵字修飾需要在命名空間之外訪問的成員如:接口和類
// a.ts namespace Shape { let pi = Math.PI; export function circle(r: number): number { return pi *r * r; } } Shape.circle(2); // 變量 pi 是實現的細節,不需要導出,因此在命名空間之外是不能訪問的 Shape.pi; // Error: 類型“typeof Shape”上不存在屬性“pi”
(2)、分離到多文件
多個文件可以共享同一個命名空間以便於維護,盡管是不同的文件,但仍是同一個命名空間。在使用的時候就如同這些文件是在一個文件中定義的一樣
// b.ts namespace Shape { export function square(width: number) { return width ** 2; } }
(3)、三斜線指令
/// <reference path="..." />
用於聲明文件之間的依賴關系,僅可放在包含它的文件最頂端,三斜線指令的前面只能出現單行或多行注釋;如果出現在一個語句或聲明之后,就會被當作普通的單行注釋,並且不具有特殊的含義。
上例中,a.ts 和 b.ts 共享同一個命名空間 Shape,在 b.ts 中可以訪問到 a.ts 的 circle方法,但是執行的時候會報錯
這個時候就需要使用三斜線指令在 b.ts 中引用 a.ts
// b.ts /// <reference path="./a.ts" /> namespace Shape { export function square(width: number) { return width ** 2; } } console.log(Shape.circle(2));
編譯結果如下:
(4)、別名
可以使用 import q = x.y.z 給常用的對象起一個短的名字
namespace Shape { let pi = Math.PI; export function circle(r: number): number { return pi *r * r; } } import circle = Shape.circle; console.log(circle(1)); // 3.141592653589793
2、聲明合並
所謂 “聲明合並” 就是編譯器會把程序中多個地方具有相同名稱的聲明合並為一個聲明,好處是可以把程序中散落各處的重名聲明合並到一起
2-1、合並接口
// a.ts interface A { name: string; } interface A { age: number; } let a: A = { name: 'Kobe', age: 41 } console.log(a); // { name: 'Kobe', age: 41 }
如果是全局模塊,接口A的聲明合並甚至可以在不同的文件中,例如新建一個 b.ts 也聲明一個接口A,原來a.ts中的變量a就不能正確輸出了
// b.ts interface A { gender: string; }
2-1-1、接口的非函數成員
接口中的非函數成員必須保持唯一性,如果不唯一的話,它們的類型必須相同
// a.ts interface A { name: string; age: number; } interface A { name: number; age: number; } // 兩個接口中,成員age的類型相同,成員name的類型不相同,編譯器提示報錯
2-1-2、接口的函數成員
接口中的每一個函數成員都會被聲明成 函數重載
// a.ts interface A { name: string; foo(x: number): number; } interface A { age: number; foo(x: string): string; foo(x: number[]): number[]; } // 實現接口A時,函數foo需要返回一個 any類型 let a: A = { name: 'Kobe', age: 41, foo(x: any) { return x } }
函數重載的時候,需要注意函數聲明的順序,因為編譯器會按順序進行匹配。那么,在接口合並時,這些順序是如何確定的呢?
(1)、在接口的內部,按照書寫的順序來確定;
// a.ts interface A { age: number; foo(x: string): string; // 1 foo(x: number[]): number[]; // 2 }
(2)、接口之間,后面的接口排在前面
// a.ts interface A { name: string; foo(x: number): number; // 3 } interface A { age: number; foo(x: string): string; // 1 foo(x: number[]): number[]; // 2 }
(3)、如果一個函數的參數是一個字符串字面量,這個聲明將會提升到整個函數聲明的最頂端
// a.ts interface A { name: string; foo(x: number): number; // 5 foo(x: 'a'): string; // 2 } interface A { age: number; foo(x: string): string; // 3 foo(x: number[]): number[]; // 4 foo(x: 'b'): string; // 1 }
2-2、合並命名空間
(1)、命名空間中導出的成員是不可以重復定義的,這一點與合並接口不同
// a.ts namespace Shape { const pi = Math.PI; export function square(r: number) { return pi * r ** 2; } } // b.ts namespace Shape { export function square(width: number) { return width * width; } }
(2)、合並命名空間和函數 --- 相當於給函數添加了屬性
// a.ts function Lib() {} namespace Lib { export let str = 'hello world'; } console.log(Lib.str); // hello world
(3)、合並命名空間和類 --- 相當於給類添加了靜態屬性
// a.ts class C {} namespace C { export let str = 'hello world'; } console.log(C.str); // hello world
注意:命名空間與函數或者類合並時,必須要放在函數或者類的后面,否則會報錯
2-3、合並枚舉
合並命名空間與枚舉,相對於給枚舉添加了屬性。同時需要注意,命名空間與枚舉的位置不受限制
// a.ts // 注意命名空間與枚舉的位置 namespace Color { export function mix() {} } enum Color { Red, Green, Blue } console.log(Color);