Typescript 實戰 --- (10)命名空間和模塊

一、命名空間函數

ts 中的 「命名空間」 就是以前的 「內部模塊」,任何使用 module 關鍵字來聲明一個內部模塊的地方都應該使用 namespace 關鍵字來替換spa

// ts 中的「內部模塊」 (廢棄)
module X {   }

// ts 中的「命名空間」 (推薦)
namespace X {   }

 

(1)、export 關鍵字3d

使用 export 關鍵字修飾須要在命名空間以外訪問的成員如:接口和類code

// 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)、分離到多文件對象

多個文件能夠共享同一個命名空間以便於維護,儘管是不一樣的文件,但還是同一個命名空間。在使用的時候就如同這些文件是在一個文件中定義的同樣blog

// b.ts


namespace Shape {
  export function square(width: number) {
    return width ** 2;
  }
}

 

(3)、三斜線指令接口

/// <reference path="..." />

用於聲明文件之間的依賴關係,僅可放在包含它的文件最頂端,三斜線指令的前面只能出現單行或多行註釋;若是出如今一個語句或聲明以後,就會被看成普通的單行註釋,而且不具備特殊的含義。ci

上例中,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-一、合併接口

// 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-一、接口的非函數成員

接口中的非函數成員必須保持惟一性,若是不惟一的話,它們的類型必須相同

// a.ts 

interface A {
  name: string;
  age: number;
}

interface A {
  name: number;
  age: number;
}

// 兩個接口中,成員age的類型相同,成員name的類型不相同,編譯器提示報錯

 

2-1-二、接口的函數成員

接口中的每個函數成員都會被聲明成 函數重載

// 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-二、合併命名空間

(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-三、合併枚舉

合併命名空間與枚舉,相對於給枚舉添加了屬性。同時須要注意,命名空間與枚舉的位置不受限制

// a.ts 

// 注意命名空間與枚舉的位置
namespace Color {
  export function mix() {}
}

enum Color {
  Red,
  Green,
  Blue
}

console.log(Color);  

相關文章
相關標籤/搜索