TypeScript Declaration Merging(声明合并)
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(声明合并)的更多相关文章
- 转载:《TypeScript 中文入门教程》 11、声明合并
版权 文章转载自:https://github.com/zhongsp 建议您直接跳转到上面的网址查看最新版本. 介绍 TypeScript有一些独特的概念,有的是因为我们需要描述JavaScript ...
- TypeScript 素描 - 模块解析、声明合并
模块解析 模块解析有两种方式 相对方式 也就是以/或 ./或-/开头的,比如import jq from "/jq" 非相对方式 比如 import model from ...
- Declaration Merging with TypeScript
原文:https://blog.oio.de/2014/03/21/declaration-merging-typescript/ Why might you need this? There can ...
- TypeScript完全解读(26课时)_16.声明合并
ts编辑器会将名字相同的多个声明合并为一个声明,合并后的声明,同时拥有多个声明的特性 example文件夹下新建merging.ts文件 定义相同名字的接口, 定义变量类型是上面的接口.,光写一个na ...
- TypeScript模块系统、命名空间、声明合并
命名空间 命名空间能有效避免全局污染.在ES6引入模块之后,命名空间就较少被提及了.如果使用了全局的类库,命名空间仍是一个好的解决方案. namespace Shape{ const pi = Mat ...
- Typescript declaration: Merge a class and an interface
参考: https://stackoverflow.com/questions/47670959/typescript-declaration-merge-a-class-and-an-interfa ...
- TypeScript 素描-变量声明
博文读自 TypeScript 官方文档而来,不具有学习性,仅是本人学习时记录以供日后翻阅 ,有学习TypeScript的朋友还请去看更为详细的官方文档 /* 变量声明在之前的js中一直是使用var关 ...
- 玩转TypeScript(5)--环境声明
环境声明为TypeScript引入了一个作用域,但是对于产生的javaScript程序不会有任何影响.程序员可以使用环境声明来告之TypeScript,一些其他的组将将提供变量的声明.比如,默认情况下 ...
- using directive 使用指令,与using declaration使用声明。
使用指令是把名字空间中的所有名字引入到当前作用域,而使用声明是把名字空间的某个名字引入到当前作用域中 语法如下 //test.cpp #include<iostream> //using ...
随机推荐
- nodejs 中自定义事件
经常看到 req.on('error', function(){...}); 这种代码. 在nodejs中,可以使用 EventEmitter来实现. 具体的关键词有如下几个: var reqEven ...
- 深入解析Windows操作系统笔记——CH3系统机制
3.系统机制 微软提供了一些基本组件让内核模式的组件使用: 1.陷阱分发,包括终端,延迟的过程调用(DPC),异步过程调用(APC),异常分发以及系统服务分发 2.执行体对象管理器 3.同步,包括自旋 ...
- .NET/ASP.NET MVC Controller 控制器(深入解析控制器运行原理)
阅读目录: 1.开篇介绍 2.ASP.NETMVC Controller 控制器的入口(Controller的执行流程) 3.ASP.NETMVC Controller 控制器的入口(Controll ...
- 实战:考虑性能--Solr索引的schema设计
从 high level 的角度来看,schema.xml 结果如下,这个例子虽然不是一个真实的XML,但是简洁明了的传达了shema的概念. <schema> <types> ...
- js Ajax跨域调用JSON并赋值全局变量
//跨域调用JSON <script type="text/javascript"> function _callback(obj) { alert(obj); } j ...
- 搭建自己的PHP框架心得(二)
h2:first-child, body>h1:first-child, body>h1:first-child+h2, body>h3:first-child, body>h ...
- 使用shell定时自动备份mysql数据库
#!/bin/bash id="root" #用户名 pwd="123456" #密码 dbs="blog.ewsd.cn dangjian.ewsd ...
- PHP笔记(PHP初级篇)
学习完HTML和CSS后,终于要开始学习PHP啦!前面的铺垫只为后路的畅顺! PHP环境搭建: 企业中常用到的环境是:Linux+Apache+MySQL+PHP 学习环境是:Windows+Apac ...
- hibernate基础dao类
此文章是基于 搭建SpringMVC+Spring+Hibernate平台 功能:数据库的保存.更新.删除:sql.hql查询:分页查询:调用存储过程 创建hibernate基础dao类: BaseD ...
- Postgresql 取随机数
取0和1之间的随机数 SELECT RANDOM(); 取介于两数之间的随机数 SELECT random()*(b-a)+a; ); 取介于两数之间的随机整数 SELECT floor(random ...