TypeScript手册翻译系列8-声明合并

声明合并(Declaration Merging)

TypeScript中一些独到的概念(unique concepts)来自于须要描述JavaScript对象的shape在type级发生了什么。其中一个例子就是声明合并('declaration merging')。理解这个概念会对TypeScript中使用现有的JavaScript带来优点,也会打开更高级的抽象概念之门。

在深刻声明如何合并以前,首先描述下什么是声明合并。

声明合并是指编译器将有相同名称的两个不一样声明合并到一个定义中,合并后的定义包含两个声明的全部特性。声明合并并不仅限于两个声明,任意数量的声明均可以合并。

typescript

基本概念(Basic Concepts)

在TypeScript中,有三个组都存在声明:namespace/module, type, 或value。建立namespace/module的声明是在写type时用点标记法来访问的。建立type的声明在声明shape并绑定到一个给定名称时是可见的。最后,建立value的声明是在output JavaScript((例如functions和variables))中可见的编程

声明类型 Namespace Type      Valuecanvas

Module    X                      X编程语言

Class                 X      X函数

Interface                 X ui

Function                     Xspa

Variable                     X.net

理解每一类声明建立了什么,将有助于理解当执行声明合并时什么被合并。
翻译

合并接口(Merging Interfaces)

最简单,多是最多见的声明合并类型就是接口合并。这种合并就是将两个声明的成员机械地合并到一个接口中,成员名称保持相同。
code

interface Box {
   height: number;
   width: number;
}

interface Box {
   scale: number;
}

var box: Box = {height: 5, width: 6, scale: 10};


接口中非函数成员必须惟一。若是两个接口都声明了有相同名字的非函数成员,编译器将报错。

对于函数成员,同名的每一个函数成员被视为描述相同函数的一个重载(overload)。值得注意的是:前面一个interface A与后一个interface A (称为A')合并时,A'的重载将比interface A有更高的优先级。 

下面是一个例子:

interface Document {
   createElement(tagName: any): Element;
}

interface Document {
   createElement(tagName: string): HTMLElement;
}

interface Document {
   createElement(tagName: "div"): HTMLDivElement;
   createElement(tagName: "span"): HTMLSpanElement;
   createElement(tagName: "canvas"): HTMLCanvasElement;
}


两个interfaces将合并建立出一个声明。注意每一个组的元素保持相同顺序,这些组合并后,后面重载集将靠前:

interface Document {
   createElement(tagName: "div"): HTMLDivElement;
   createElement(tagName: "span"): HTMLSpanElement;
   createElement(tagName: "canvas"): HTMLCanvasElement;
   
   createElement(tagName: string): HTMLElement;
   
   createElement(tagName: any): Element;
}

合并模块(Merging Modules)

相似于接口,同名的模块也合并成员。由于模块会建立namespace与value,咱们须要理解这两种是如何合并的。

为了合并namespaces,在每一个模块中声明的exported接口类型定义被合并, 造成一个namespace,其中有合并后的接口定义。
为了合并value,在每一个声明site,若是存在同名的module,就扩展先前的module,将第二个module的exported成员添加到第一个module中。

在这个例子中,合并'Animals'声明

module Animals {    
   export class Zebra { }
}

module Animals {    
   export interface Legged { numberOfLegs: number; }    
   export class Dog { }
}


等同于:

module Animals {    
   export interface Legged { numberOfLegs: number; }
   
   export class Zebra { }    
   export class Dog { }
}


上面这个模块合并模型是一个很是好的起点,但为了更完整地理解,就须要理解非exported成员发生了什么。exported成员只能在原有的(未合并)模块中可见。这意味着在合并后,从其余声明来的合并成员不能看到这些exported成员。

在这个例子中能够看得更为清楚:

module Animal {    
   var haveMuscles = true;
   
   export function animalsHaveMuscles() {        
       return haveMuscles;
   }
}

module Animal {    
   export function doAnimalsHaveMuscles() {        
       return haveMuscles;  // <-- error, haveMuscles is not visible here
   }
}


由于haveMuscles没有exported,只有在未合并的module中的animalsHaveMuscles函数才能看到这个符号。doAnimalsHaveMuscles函数虽然是合并后的Animal模块的一部分,但它不能看到这个非exported成员。

合并模块与类、函数以及枚举(Merging Modules with Classes, Functions, and Enums)

模块很是灵活,还能够与其余类型的声明作合并。为此,模块声明必须放在它将合并的声明后面。最终的声明包含两种声明类型的全部属性。TypeScript利用这种能力来建模JavaScript以及其余编程语言中的一些patterns。

咱们讨论的第一个模块合并是合并模块与类,这样就能够描述内部类(inner class)。

class Album {
   label: Album.AlbumLabel;
}

module Album {    
   export class AlbumLabel { }
}


合并后成员的可见性规则与"合并模块"章节中的规则相同,因此咱们必须export AlbumLabel类,以便合并后的类可看到它。最终结果是一个类在另外一个类中被管理。也能够用modules将更多的静态成员添加到一个类中。

除了内部类(inner class)这个模式之外,你可能也熟悉JavaScript中建立函数,而后向这个函数添加属性来扩展它。TypeScript使用声明合并来构建相似的定义,可是以type-safe方式来构建。 

function buildLabel(name: string): string {    
   return buildLabel.prefix + name + buildLabel.suffix;
}    

module buildLabel {    
   export var suffix = "";    
   export var prefix = "Hello, ";
}

alert(buildLabel("Sam Smith"));


相似地,modules能够用静态成员来扩展枚举:

enum Color {
   red = 1,
   green = 2,
   blue = 4
}

module Color {    
   export function mixColor(colorName: string) {        
       if (colorName == "yellow") {            
           return Color.red + Color.green;
       }        
       else if (colorName == "white") {            
           return Color.red + Color.green + Color.blue;
       }        
       else if (colorName == "magenta") {            
           return Color.red + Color.blue;
       }        
       else if (colorName == "cyan") {            
           return Color.green + Color.blue;
       }
   }
}

不容许的合并(Disallowed Merges)

TypeScript中并不是全部的合并都容许。如今,classes不能与其余classes合并,variables与classes不能合并,interfaces与classes不能合并。 关于模仿classes合并信息可参见Mixins in TypeScript 章节。

参考资料

[1] http://www.typescriptlang.org/Handbook#declaration-merging

[2] TypeScript系列1-简介及版本新特性, http://my.oschina.net/1pei/blog/493012

[3] TypeScript手册翻译系列1-基础类型, http://my.oschina.net/1pei/blog/493181

[4] TypeScript手册翻译系列2-接口, http://my.oschina.net/1pei/blog/493388

[5] TypeScript手册翻译系列3-类, http://my.oschina.net/1pei/blog/493539

[6] TypeScript手册翻译系列4-模块, http://my.oschina.net/1pei/blog/495948

[7] TypeScript手册翻译系列5-函数, http://my.oschina.net/1pei/blog/501273

[8] TypeScript手册翻译系列6-泛型, http://my.oschina.net/1pei/blog/513483

[9] TypeScript手册翻译系列7-常见错误与mixins, http://my.oschina.net/1pei/blog/513499

相关文章
相关标签/搜索