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(混合)

时间: 2024-11-04 06:11:42

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

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

版权 文章转载自:https://github.com/zhongsp 建议您直接跳转到上面的网址查看最新版本. 介绍 TypeScript有一些独特的概念,有的是因为我们需要描述JavaScript顶级对象的类型发生了哪些变化. 这其中之一叫做声明合并. 理解了这个概念,对于你使用TypeScript去操作现有的JavaScript来说是大有帮助的. 同时,也会有助于理解更多高级抽象的概念. 首先,在了解如何进行声明合并之前,让我们先看一下什么叫做声明合并. 在这个手册里,声明合并是指编译器会

TypeScript声明合并

介绍 TypeScript中有些独特的概念可以在类型层面上描述JavaScript对象的模型. 这其中尤其独特的一个例子是“声明合并”的概念. 理解了这个概念,将有助于操作现有的JavaScript代码. 同时,也会有助于理解更多高级抽象的概念. 对本文件来讲,“声明合并”是指编译器将针对同一个名字的两个独立声明合并为单一声明. 合并后的声明同时拥有原先两个声明的特性. 任何数量的声明都可被合并:不局限于两个声明. 基础概念 TypeScript中的声明会创建以下三种实体之一:命名空间,类型或值

Declaration Merging with TypeScript

原文:https://blog.oio.de/2014/03/21/declaration-merging-typescript/ Why might you need this? There can be several scenarios where this might be required. One of the most common ones is when you want to extend an existing JavaScript library that comes w

TypeScript的变量声明

1.全新的变量声明方式 let和const是JavaScript ES6中新添加的变量声明方式.let在很多方面与var是相似的,但是它可以避免一些在JavaScript里常见一些问题. 而const则是对let的一个增强,它将阻止对一个变量再次赋值(二次赋值). 因为TypeScript是JavaScript的超集,所以它本身就支持let和const. ⒉var声明 原文地址:https://www.cnblogs.com/fanqisoft/p/11828839.html

TypeScript手册翻译系列12-编写.d.ts文件

Writing .d.ts files When using an external JavaScript library, or new host API, you'll need to use a declaration file (.d.ts) to describe the shape of that library. This guide covers a few high-level concepts specific to writing definition files, the

TypeScript: type alias 与 interface

官方文档中有关于两者对比的信息,隐藏在 TypeScript Handbook 中,见 Interfaces vs. Type Aliases 部分. 但因为这一部分很久没更新了,所以其中描述的内容不一定全对. 比如, 区别点之一:Type Alias 不会创建新的类型,体现在错误信息上. One difference is, that interfaces create a new name that is used everywhere. Type aliases don't create

1.初步认识TypeScript

简介:typescript是C#之父主导的一门语言,本质上是向Javascript语言添加了可选的静态类型和基于面向对象的诸多特性.相当于javascript的超集,其包含es6.由于是和C#之父创造的,所以这里我采用和C#对比的方式学习他们之间的不同点,和主流面向对象语言(C#,Java)中相符的性质将不作记录. 1. 数字类型:C#有一系列限定大小范围的int,short,int16,long等整形,还有float,double等小数类型,而ts(TypeScript简称,为方便,以下皆简称

TypeScript中文手册

TypeScript Handbook(中文版) TypeScript 2.1 正式发布! TypeScript是Microsoft公司注册商标. TypeScript具有类型系统,且是JavaScript的超集. 它可以编译成普通的JavaScript代码. TypeScript支持任意浏览器,任意环境,任意系统并且是开源的. TypeScript目前还在积极的开发完善之中,不断地会有新的特性加入进来. 因此本手册也会紧随官方的每个commit,不断地更新新的章节以及修改措词不妥之处. 如果你

转载:TypeScript 简介与《TypeScript 中文入门教程》

简介 TypeScript是一种由微软开发的自由和开源的编程语言.它是JavaScript的一个超集,而且本质上向这个语言添加了可选的静态类型和基于类的面向对象编程.安德斯·海尔斯伯格,C#的首席架构师,已工作于TypeScript的开发. TypeScript扩展了 JavaScript 的句法,所以任何现有的JavaScript程序可以不加改变的在TypeScript下工作.TypeScript是为大型应用之开发而设计,而编译时它产生 JavaScript 以确保兼容性. TypeScrip