聲明合并(declaration merging)指的是 TSC 將多個(gè)同名的聲明合并到同一個(gè)定義中。
基本概念
TS 中有 namespace,type,value 三種實(shí)體。聲明便是創(chuàng)建實(shí)體。
合并接口
例如:
interface Box{
height: number;
width: number;
}
interface Box{
scale: number;
}
let box: Box = {height: 5, width: 6, scale: 10};
- 如果多個(gè)接口中的同名屬性類型不一致的話,編譯器會(huì)報(bào)錯(cuò)。
- 方法的合并一般是后面的聲明排在前面的重載,如果是參數(shù)是字符串字面量則會(huì)提前。
如
interface Document {
createElement(tagName: any): Element;
}
interface Document {
createElement(tagName: "div"): HTMLDivElement;
createElement(tagName: "span"): HTMLSpanElement;
}
interface Document {
createElement(tagName: string): HTMLElement;
createElement(tagName: "canvas"): HTMLCanvasElement;
}
會(huì)合并成:
interface Document {
createElement(tagName: "canvas"): HTMLCanvasElement;
createElement(tagName: "div"): HTMLDivElement;
createElement(tagName: "span"): HTMLSpanElement;
createElement(tagName: string): HTMLElement;
createElement(tagName: any): Element;
}
命名空間的合并
合并之后,原來命名空間沒有 export 的東西,不能其他同名命名空間直接訪問。
合并 帶類,函數(shù),枚舉的命名空間。
class Album{
label: Album.AlbumLabel;
}
namespace Album{
export class AlbumLabel{}
}
將編譯成如下代碼:
var Album = /** @class */ (function () {
function Album() {
}
return Album;
}());
(function (Album) {
var AlbumLabel = /** @class */ (function () {
function AlbumLabel() {
}
return AlbumLabel;
}());
Album.AlbumLabel = AlbumLabel;
})(Album || (Album = {}));
所以也可以借助命名空間的合并以實(shí)現(xiàn)擴(kuò)展已有的類,函數(shù),枚舉等功能。
比如擴(kuò)展函數(shù) :
function buildLabel(name:string):string{
return buildLabel.prefix + name + buildLabel.suffix;
}
namespace buildLabel{
export let suffix = "";
export let prefix = "hello,";
}
?
編譯成:
function buildLabel(name) {
return buildLabel.prefix + name + buildLabel.suffix;
}
(function (buildLabel) {
buildLabel.suffix = "";
buildLabel.prefix = "hello,";
})(buildLabel || (buildLabel = {}));
合并的限制,類不能跟類及其他變量合并。
模塊增強(qiáng)
在 JS 中通過導(dǎo)入對(duì)象并更新。
如:
// observable.js
export class Observable<T> {}
// map.js
import { Observable } from "./observable";
Observable.prototype.map = function (f) {}
TS 中也可以,不過編譯器不知道 Observable.prototype.map 需要通過模塊增強(qiáng)來聲明:
// observable.ts 同上
// map.ts
import { Observable } from "./observable";
declare module "./observable" {
interface Observable<T> {
map<U>(f: (x: T) => U): Observable<U>;
}
}
Observable.prototype.map = function (f) {
}
// consumer.ts
import { Observable } from "./observable";
import "./map";
let o: Observable<number>;
o.map(x => x.toFixed());
原來的模塊正常解析之后,在增強(qiáng)的聲明會(huì)被合并就好比像在同一個(gè)文件中聲明一樣。不過不能聲明新的頂級(jí)聲明。只能修改已有的聲明。
全局增強(qiáng)
也可以在模塊內(nèi)部為全局作用域添加聲明。
示例如下:
// observable.ts
export class Observable<T>{
}
declare global{
interface Array<T>{
toObservable(): Observable<T>;
}
}
Array.prototype.toObservable = function(){
}