1. 程式人生 > >TypeScript模組系統、名稱空間、宣告合併

TypeScript模組系統、名稱空間、宣告合併

名稱空間

名稱空間能有效避免全域性汙染。在ES6引入模組之後,名稱空間就較少被提及了。如果使用了全域性的類庫,名稱空間仍是一個好的解決方案。

namespace Shape{
   const pi = Math.PI;
   // 使用export關鍵字匯出,可以在全域性空間內可見
   export function circle(r: number) {
      return pi * r ** 2
   }
   square(5)
}
Shape.circle(10); // 可以在全域性空間訪問匯出的
import circle = Shape.circle; // 為名稱空間內的變數起個別名,要清楚此處import與模組化的import含義不一樣
circle(20);

隨著程式的擴張,名稱空間也會很大,需要對其進行拆分,在不同的檔案中使用同名名稱空間,他們之間共享名稱空間。

// space1.ts
/// <reference path="space2.ts" />  // 三斜線引用標籤告訴編譯器,兩個檔案中的名稱空間內部存在著依賴關係
namespace Shape{
   export function square(x: number) {
      return x*x;
   }
   circle(10); // 只有circle被export後,這裡才能訪問
}

// space2.ts
/// <reference path="space1.ts" />
namespace Shape{
   const pi = Math.PI;
   // 使用export關鍵字匯出,可以在全域性空間內可見
   export function circle(r: number) {
      return pi * r ** 2
   }
   square(5); 
}

名稱空間最好不要和模組一起混用

模組化系統

TypeScript對ES6和CommonJS兩種模組系統都有很好的支援,我們基本可以沿用以前的寫法。但兩者不要混用,如果出現混用,就要使用TS準備的相容性寫法。

先來看看ES6和CommonJS各自的寫法

// ES6匯入
import { a, b } from './Modular System/es6/a';
import { a as f } from './Modular System/es6/a';
import * as All from './Modular System/es6/a';
import abc from './Modular System/es6/b';
import Obj from './Modular System/es6/a'
// ES6匯出
export defalut Obj;
export {a,b,c};
export {d as D};
export {D as C}  from './a'; // 將a.ts中的D重新命名並匯出,這種用法只能對a.ts中的非預設匯出有效

// CommonJS匯入
let c1 = require('./Modular System/node/a.ts');
let c2 = require('./Modular System/node/b');
// CommonJS匯出
module.exports = a;  // 將a變數匯出
exports.c = 3;
exports.d = 4;
相當於
module.exports = {c:3, d:4}
如果兩種方式並存,module.exports將會覆蓋exports.c這種方式的匯出

兩種模組在匯入匯出時互不相容:

  • 匯出:ES6允許同時存在export default和export多個變數,而CommonJS只允許有一種形式的匯出,其中一種會把另外一種覆蓋掉。
  • 匯入:ES6可以按需匯入也可以全部匯入,而CommonJS只能全部匯入。

如果在ES6模組中丟擲資料,在非ES6模組中匯入,就會出現問題。因此儘量不要混用不同的模組化系統。如果迫不得已,可以使用TS提供的相容性語法:

// 匯出
export = a; 
// 匯入
import c4 = require('../es6/c');
/* 
1.如果使用以上方法匯出,此檔案不允許有其它形式的匯出 
2.以上形式的匯出的資料,不僅可以用以上語法匯入,還可以用es6的方式匯入。前提是tsconfig.json中的"esModuleInterop":true配置項要開啟。
*/

宣告合併

 編譯器會把程式的多個地方具有相同名稱的宣告合併成一個,這樣可以將程式散落在各處的重名宣告合併在一起。

例如:

interface StateMerge {
   x: number,
   y: string,
}
interface StateMerge {
   y: string;
   foo(bar: string[]): string[], 
}
// 此時會將兩個宣告的同名介面成員合併
let stateMerge: StateMerge = {
   x: 1,
   y: "15",
   foo(bar: any) {
      return bar
   }
};

如果合併的兩個結構內成員重名怎麼辦?

  • 對於非函式成員,必須型別一致,否則報錯。
  • 對於函式成員,會發生過載,過載的順序按照以下規則。
interface StateMerge {
   x: number,
   y: string,
   foo(bar: number): number; // 4
   foo(bar: string): string; // 5
   foo(bar: "b"): number;    // 2
}
interface StateMerge {
   y: string;
   foo(bar: string[]): string[], // 3
   foo(bar: "a"): number,  // 1
}

介面內部按照先後順序。介面之間,宣告在後的介面函式成員排序更靠前。

如果出現自變數,排名最靠前。後面的介面中的排在第一位,前面的介面排在第二位。上述排序如註釋所示。

函式和名稱空間的合併

function Lib() {
}
namespace Lib{
   export let version = '1.0'
}
console.log(Lib.version); // 相當於為函式Lib添加了屬性

類和名稱空間的合併

class C{
}
namespace C{
   export let state = 1
}
console.log(C.state); // 相當於為類C添加了state屬性

此外,還可以為列舉增加屬性。

注意:在名稱空間與類、函式進行生命合併的時候,一定要將名稱空間放在類、函式之後。否則報錯。

&n