声明合并(declaration merging)指的是 TSC 将多个同名的声明合并到同一个定义中。
基本概念
TS 中有 namespace
,type
,value
三种实体。声明便是创建实体。
合并接口
例如:
interface Box{
height: number;
width: number;
}
interface Box{
scale: number;
}
let box: Box = {height: 5, width: 6, scale: 10};
- 如果多个接口中的同名属性类型不一致的话,编译器会报错。
- 方法的合并一般是后面的声明排在前面的重载,如果是参数是字符串字面量则会提前。
如
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;
}
会合并成:
interface Document {
createElement(tagName: "canvas"): HTMLCanvasElement;
createElement(tagName: "div"): HTMLDivElement;
createElement(tagName: "span"): HTMLSpanElement;
createElement(tagName: string): HTMLElement;
createElement(tagName: any): Element;
}
命名空间的合并
合并之后,原来命名空间没有 export
的东西,不能其他同名命名空间直接访问。
合并 带类,函数,枚举的命名空间。
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 = {}));
所以也可以借助命名空间的合并以实现扩展已有的类,函数,枚举等功能。
比如扩展函数 :
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 = {}));
合并的限制,类不能跟类及其他变量合并。
模块增强
在 JS 中通过导入对象并更新。
如:
// observable.js
export class Observable<T> {}
// map.js
import { Observable } from "./observable";
Observable.prototype.map = function (f) {}
TS 中也可以,不过编译器不知道 Observable.prototype.map
需要通过模块增强来声明:
// 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());
原来的模块正常解析之后,在增强的声明会被合并就好比像在同一个文件中声明一样。不过不能声明新的顶级声明。只能修改已有的声明。
全局增强
也可以在模块内部为全局作用域添加声明。
示例如下:
// observable.ts
export class Observable<T>{
}
declare global{
interface Array<T>{
toObservable(): Observable<T>;
}
}
Array.prototype.toObservable = function(){
}
网友评论