总结
- 同名的命名空间、模块下
- 接口支持合并。
- 枚举的实现和接口支持合并。
- 表示函数的接口在合并时有比较特殊的规则,其他合并规则都遵循后合并的在前。
- 命名空间在模块体系中不推荐使用,模块已经原生支持了,也不建议通过 module 创建。所以这个规则没有太大的意义。只适合用来拓展类型,而不适合拓展实现。 - 命名空间和模块本身也类型和实现的合并
- 会按类型种类进行合并,先合并接口后合并 class,其他顺序不清楚,似乎也不关心
- 非导出成员在各自命名空间、模块中是独立的。
- 实现的合并没有什么用,模块模式中不建议使用命名空间。适用于扩展外部模块的类型。 - 命名空间还能够和 class、函数的实现进行合并
- 命名空间和 class 必须同名
- 命名空间必须在 class 后定义
- 命名空间扩展了 class 的静态端,即給一个函数对象添加了某些属性。 - 命名空间还能够和枚举的实现进行合并,即给枚举对象扩展了属性。
原文地址 www.tslang.cn
介绍
对本文件来讲,“声明合并” 是指编译器将针对同一个名字的两个独立声明合并为单一声明。 合并后的声明同时拥有原先两个声明的特性。 任何数量的声明都可被合并;不局限于两个声明。
基础概念
TypeScript 中的声明会创建以下三种实体之一:命名空间,类型或值。 创建命名空间的声明会新建一个命名空间,它包含了用(.)符号来访问时使用的名字。 创建类型的声明是:用声明的模型创建一个类型并绑定到给定的名字上。 最后,创建值的声明会创建在 JavaScript 输出中看到的值。
注释:表格中的 X 是指拥有。
Declaration Type | Namespace | Type | Value |
---|---|---|---|
Namespace | X | X | |
Class | X | X | |
Enum | X | X | |
Interface | X | ||
Type Alias | X | ||
Function | X | ||
Variable | X |
合并接口
最简单也最常见的声明合并类型是接口合并。 从根本上说,合并的机制是把双方的成员放到一个同名的接口里。
注释:一个模块中允许有同名的接口
interface Box {
height: number;
number;
}
interface Box {
scale: number;
}
let box: Box = {height: 5, 6, scale: 10};
接口的非函数的成员应该是唯一的。如果它们不是唯一的,那么它们必须是相同的类型。如果两个接口中同时声明了同名的非函数成员且它们的类型不同,则编译器会报错。
注释:以下为用接口声明函数的类型
对于函数成员,每个同名函数声明都会被当成这个函数的一个重载。 同时需要注意,当接口 A
与后来的接口 A
合并时,后面的接口具有更高的优先级。
如下例所示:
interface Cloner {
clone(animal: Animal): Animal;
}
interface Cloner {
clone(animal: Sheep): Sheep;
}
interface Cloner {
clone(animal: Dog): Dog;
clone(animal: Cat): Cat;
}
这三个接口合并成一个声明:
interface Cloner {
clone(animal: Dog): Dog;
clone(animal: Cat): Cat;
clone(animal: Sheep): Sheep;
clone(animal: Animal): Animal;
}
注意每组接口里的声明顺序保持不变,但各组接口之间的顺序是后来的接口重载出现在靠前位置。
这个规则有一个例外是当出现特殊的函数签名时。 如果签名里有一个参数的类型是 _单一_的字符串字面量(比如,不是字符串字面量的联合类型),那么它将会被提升到重载列表的最顶端。
比如,下面的接口会合并到一起:
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;
}
合并后的 Document
将会像下面这样:
interface Document {
createElement(tagName: "canvas"): HTMLCanvasElement;
createElement(tagName: "div"): HTMLDivElement;
createElement(tagName: "span"): HTMLSpanElement;
createElement(tagName: string): HTMLElement;
createElement(tagName: any): Element;
}
合并命名空间
与接口相似,同名的命名空间也会合并其成员。 命名空间会创建出命名空间和值,我们需要知道这两者都是怎么合并的。
对于命名空间的合并,模块导出的同名接口进行合并,构成单一命名空间内含合并后的接口。
对于命名空间里值的合并,如果当前已经存在给定名字的命名空间,那么后来的命名空间的导出成员会被加到已经存在的那个模块里。
Animals
声明合并示例:
namespace Animals {
export class Zebra { }
}
namespace Animals {
export interface Legged { numberOfLegs: number; }
export class Dog { }
}
等同于:
namespace Animals {
export interface Legged { numberOfLegs: number; }
export class Zebra { }
export class Dog { }
}
除了这些合并外,你还需要了解非导出成员是如何处理的。 非导出成员仅在其原有的(合并前的)命名空间内可见。这就是说合并之后,从其它命名空间合并进来的成员无法访问非导出成员。
下例提供了更清晰的说明:
namespace Animal {
let haveMuscles = true;
export function animalsHaveMuscles() {
return haveMuscles;
}
}
namespace Animal {
export function doAnimalsHaveMuscles() {
return haveMuscles; // Error, because haveMuscles is not accessible here
}
}
命名空间与类和函数和枚举类型合并
命名空间可以与其它类型的声明进行合并。 只要命名空间的定义符合将要合并类型的定义。合并结果包含两者的声明类型。 TypeScript 使用这个功能去实现一些 JavaScript 里的设计模式。
注释:命名空间和 class 必须同名,class 内可以通过 命名空间. 来访问命名空间中的类型和实例;
合并命名空间和类
这让我们可以表示内部类。
class Album {
label: Album.AlbumLabel;
}
namespace Album {
export class AlbumLabel { }
}
合并规则与上面 合并命名空间
小节里讲的规则一致,我们必须导出 AlbumLabel
类,好让合并的类能访问。 合并结果是一个类并带有一个内部类。 你也可以使用命名空间为类增加一些静态属性。
注释:以下例子更能表现命名空间和 class 合并的作用,用于扩展类的静态端
class Album2 {} namespace Album2 { export const AlbumLabel = "1"; } console.log(Album2.AlbumLabel,new Album2());
注释:以下为合并函数和命名空间
除了内部类的模式,你在 JavaScript 里,创建一个函数稍后扩展它增加一些属性也是很常见的。 TypeScript 使用声明合并来达到这个目的并保证类型安全。
function buildLabel(name: string): string {
return buildLabel.prefix + name + buildLabel.suffix;
}
namespace buildLabel {
export let suffix = "";
export let prefix = "Hello, ";
}
console.log(buildLabel("Sam Smith"));
注释:以下为合并枚举和命名空间,可以扩展枚举类的功能。
相似的,命名空间可以用来扩展枚举型:
enum Color {
red = 1,
green = 2,
blue = 4
}
namespace 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;
}
}
}
非法的合并
TypeScript 并非允许所有的合并。 目前,类不能与其它类或变量合并。 想要了解如何模仿类的合并,请参考 TypeScript 的混入。