命名空間
命名空間能有效避免全局污染。在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屬性
此外,還可以為枚舉增加屬性。
注意:在命名空間與類、函數進行生命合並的時候,一定要將命名空間放在類、函數之后。否則報錯。
