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 学习随笔-系统日常管理常用命令

    1:W 查看系统整体负载,无法查看具体负载,比如内存,磁盘  23:25:20 up 13 min,  2 users,  load average: 0.00, 0.01, 0.01 USER   ...

  2. ORACLE表空间管理维护

    1:表空间概念 在ORACLE数据库中,所有数据从逻辑结构上看都是存放在表空间当中,当然表空间下还有段.区.块等逻辑结构.从物理结构上看是放在数据文件中.一个表空间可由多个数据文件组成. 如下图所示, ...

  3. SQL Server高级查询

    简介 关于数据库,我们经常会听说"增查删改"之类的词语,听起来很简单,但是如果想要准确的获取到需要的数据的话,还是要花点功夫的.下面由我来和大家谈谈高级查询的用法以及和普通查询的区 ...

  4. HTML5 兼容IE浏览器

    <!doctype html> <html> <head> <meta charset="utf-8"> <title> ...

  5. 搭建 OpenStack 实验环境 - 每天5分钟玩转 OpenStack(16)

    在学习 OpenStack 各服务之前,让我们先搭建起一个实验环境. 毋庸置疑,一个看得到摸得着而且允许我们随便折腾的 OpenStack 能够提高我们的学习效率. 因为是我们自己学习用的实验环境,C ...

  6. 在macOS Sierra 10.12搭建PHP开发环境

    macOS Sierra 11.12 已经帮我们预装了 Ruby.PHP(5.6).Perl.Python 等常用的脚本语言,以及 Apache HTTP 服务器.由于 nginx 既能作为 HTTP ...

  7. Genymotion的安装与eclipse配置教程

    第一部分安装Genymotion和VirtualBox虚拟机 1,Genymotion模拟器可以在官网下载,但是下载前需要注册账号,账号注册后登陆,点击Download. 2,选择第一个with Vi ...

  8. WSTMall网站系统最新官方版

    WSTMall V1.0是在thinkphp 的经典版本3.2.2基础上进行优化开发的, TP 3.2.2不是thinkphp的一个最新的版本,却是thinkphp最金典的一个版本,正所谓站在巨人的肩 ...

  9. RabbitMQ服务安装配置

    RabbitMQ是流行的开源消息队列系统,是AMQP(Advanced Message Queuing Protocol高级消息队列协议)的标准实现,用erlang语言开发.RabbitMQ据说具有良 ...

  10. Web报表工具FineReport中JavaScript的使用

    报表软件FineReport采用的是jQuery v1.9.2框架,jQuery是一个快速的,简洁的JavaScript库,能让用户更方便地处理HTML documents.events,实现动画效果 ...