TypeScript中有一些独特的概念,来自需要描述JavaScript对象类型发生了哪些变化。举个例子,最为独特的概念就是"声明合并"。理解了这个概念将会对你在当前JavaScript项目中使用TypeScript开发很有帮助。同时也打开了了解更高级抽象概念的门。

就本文目的而言,声明合并是指编译器执行将两个名称相同的声明合并到一个单独的声明里的工作。合并后的声明具有两种原始声明的特性。当然,声明合并不限于合并两个声明,需要合并的声明数量可任意(注意:他们之间具有相同名称)。

基本概念

在TypeScript中,一个声明可以有三种情况:命名空间/模块(命名空间:内部模块;模块:外部模块)、类型、值。当声明是创建一个命名空间/模块的时候,该命名空间可通过点符号(.)来访问。创建类型的声明将会创建一个指定名称和结构的类型。最后,声明值就是那些在输出的JavaScript中可见的部分(如,函数和变量)。

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;
width: number;
} interface Box {
scale: number;
} var box: Box = {height: 5, width: 6, scale: 10};

接口中的非函数成员必须是唯一的,如果两个/多个接口同时声明相同名称的非函数成员,编译器就会扔出一个错误。

对于函数成员,相同名称的函数成员被视为这个函数的重载。值的注意的是,接口A和后面的接口A(这里成为A')合并,接口A‘中重载的函数将会比接口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;
}

这三个接口将被合并到一个单独的声明。注意,每组接口内部的顺序依旧保持相同,只是每个组之间被合并,并且排在后面的接口的成员在新声明中被放到前面。

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

模块合并

类似于接口,相同名称的模块也会对其成员进行合并。由于模块会创建一个命名空间和一个值,我们需要理解它们是如何合并的。

合并命名空间的时候,每个模块声明的输出接口的类型定义将进行合并,同名命名空间合并成一个单独的内部包含合并后接口的命名空间。

合并值的时候,如果已存在一个给定名称模块,那么后面的模块内的输出成员将被添加到这个模块。

看看这个例子中的Animal模块的声明合并:

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 { }
}

这个案例是学习模块合并很好的开始,但是想要更完整的理解,我们还需要理解非导出成员发生了什么。非导出成员只在原始(未合并)模块可见。这意味着,在合并之后,来自其他声明的合并后成员不能访问到非导出成员。

我们可以看个详细的解释:

module Animal {
var haveMuscles = true; export function animalsHaveMuscles() {
return haveMuscles;
}
} module Animal {
export function doAnimalsHaveMuscles() {
return haveMuscles; // <-- 错误, haveMuscles在这里不能访问
}
}

因为haveMuscles这个成员未被输出,只有与其共享未合并的模块的animalsHaveMuscles函数能够访问这个symbol。尽管doAnimalsHaveMuscles函数是合并后的模块的一部分,但它还是不能访问到这个其他被合并的同名模块内的未输出成员。

模块与类、函数、枚举的合并

模块具有足够的灵活性,它可以与其他类型的声明进行合并。模块的声明必须遵循与其合并的声明。最终合并后的声明将包含两个声明类型的属性。Typescript使用这个功能去实现一些JavaScript里的模式。

第一个模块合并案例,我们将模块和类合并。这给用户提供了描述内部类的方法:

class Album {
label: Album.AlbumLabel;
}
module Album {
export class AlbumLabel{
name:string;
show(){
console.log(this.name);
}
constructor(name:string){
this.name = name;
}
}
}
var newAlbum = new Album.AlbumLabel("Ys");
newAlbum.show();

合并后成员的可访问性规则和上一节的"模块合并"一样,所以我们必须export AlbumLabel类,为了让合并后的类可访问它。最终的结果是一个类的内部存在另一个类。你也可以使用模块现有的类添加更多的静态成员。

class Test{
fn:Test.TestFn
}
module Test {
export var Value:string = "World";
export class TestFn{
show(name:string){
console.log(name+" "+Value);
}
}
}
var newTest = new Test.TestFn();
newTest.show("Hello");

除了内部类的模式,你可能对JavaScript中创建一个函数稍后再扩展其属性的做法已经很熟悉了。TypeScript使用声明合并达到这个目的,并且确保了类型安全。

function buildLabel(name: string): string {
return buildLabel.prefix + name + buildLabel.suffix;
} module buildLabel {
export var suffix = "";
export var prefix = "Hello, ";
} alert(buildLabel("Sam Smith"));

同样,模块也可以用来扩展枚举的静态成员:

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;
}
}
}
alert(Color.mixColor("yellow"));

不被允许的合并
在TypeScript中,并非所有的合并都被允许。目前为止,类不能与类合并,变量和类不能合并,接口和类也不能合并。需要模仿类的合并,请参考上一节:Typescript Mixins(混合)

TypeScript Declaration Merging(声明合并)的更多相关文章

  1. 转载:《TypeScript 中文入门教程》 11、声明合并

    版权 文章转载自:https://github.com/zhongsp 建议您直接跳转到上面的网址查看最新版本. 介绍 TypeScript有一些独特的概念,有的是因为我们需要描述JavaScript ...

  2. TypeScript 素描 - 模块解析、声明合并

    模块解析 模块解析有两种方式 相对方式  也就是以/或 ./或-/开头的,比如import jq  from "/jq" 非相对方式  比如 import model  from ...

  3. Declaration Merging with TypeScript

    原文:https://blog.oio.de/2014/03/21/declaration-merging-typescript/ Why might you need this? There can ...

  4. TypeScript完全解读(26课时)_16.声明合并

    ts编辑器会将名字相同的多个声明合并为一个声明,合并后的声明,同时拥有多个声明的特性 example文件夹下新建merging.ts文件 定义相同名字的接口, 定义变量类型是上面的接口.,光写一个na ...

  5. TypeScript模块系统、命名空间、声明合并

    命名空间 命名空间能有效避免全局污染.在ES6引入模块之后,命名空间就较少被提及了.如果使用了全局的类库,命名空间仍是一个好的解决方案. namespace Shape{ const pi = Mat ...

  6. Typescript declaration: Merge a class and an interface

    参考: https://stackoverflow.com/questions/47670959/typescript-declaration-merge-a-class-and-an-interfa ...

  7. TypeScript 素描-变量声明

    博文读自 TypeScript 官方文档而来,不具有学习性,仅是本人学习时记录以供日后翻阅 ,有学习TypeScript的朋友还请去看更为详细的官方文档 /* 变量声明在之前的js中一直是使用var关 ...

  8. 玩转TypeScript(5)--环境声明

    环境声明为TypeScript引入了一个作用域,但是对于产生的javaScript程序不会有任何影响.程序员可以使用环境声明来告之TypeScript,一些其他的组将将提供变量的声明.比如,默认情况下 ...

  9. using directive 使用指令,与using declaration使用声明。

    使用指令是把名字空间中的所有名字引入到当前作用域,而使用声明是把名字空间的某个名字引入到当前作用域中 语法如下 //test.cpp #include<iostream> //using ...

随机推荐

  1. Linux 磁盘自检介绍

    在Linux系统中,有时候重启会耗费非常长的时间,如果你进一步检查细节,就会发现绝大部分时间都耗费在磁盘自检(fsck)上了,有时候遇到时间比较紧急的情况,磁盘自检耗费的时间非常长,真的是让人心焦火急 ...

  2. spark streaming 与 kafka 结合使用的一些概念理解

    1. createStream会使用 Receiver:而createDirectStream不会,数据会通过driver接收. 2.createStream使用 Receiver 源源不断的接收数据 ...

  3. Oracle触发器原理、创建、修改、删除

    本篇主要内容如下: 8.1 触发器类型 8.1.1 DML触发器 8.1.2 替代触发器 8.1.3 系统触发器 8.2 创建触发器 8.2.1 触发器触发次序 8.2.2 创建DML触发器 8.2. ...

  4. webapi 控制器接收POST参数时必须以对象的方式接收

    webapi    控制器接收POST参数时必须以对象的方式接收

  5. ORACLE 字符串超长问题解决方案

    前两天我在工作中遇到这样一个问题,我们有一个程序是用来增量抽取EBS 中的表数据的,有的是全量抽取,即先删除原表中的数据,然后重新抽取数据,示例代码如下: truncate table ods_emp ...

  6. 致命错误: zlib.h:没有那个文件或目录

    下面这个错误是因为zlib包没有安装,安装后问题即可解决.但有一点请注意安装命令是:sudo apt-get install zlib1g-dev,而非sudo apt-get install zli ...

  7. mybatis配置-返回date类型丢失时间

    此博客仅作于平时开发所遇到的问题记录,不做他用,描述可能不好,自己看懂即可~~ resultMap配置返回时间类型时,发现数据库时间是精确到秒的,但是返回给javabean之后丢失时分秒的信息,只有日 ...

  8. hammer.js的六大事件

    1.Pan事件:在指定的dom区域内,一个手指放下并移动事件,即触屏中的拖动事件.这个事件在触屏开发中比较常用: Panstart 拖动开始 Panmove 拖动过程 Panend 拖动结束 Panc ...

  9. shell parameter expansitions

    type testtype -a test math calculate:echo $((1+2*3)) parameter expansition:bash-4 introduced feature ...

  10. TAR,JAR,Zip的使用

    在文件归档的时候,LINUX中,我常喜欢使用tar,它可以把一个文件夹归档为一个文件,可以同时使用指定的压缩算法把其压缩归档. 最常用的语句是: tar cvzf target.tar.gz sour ...