我的编程空间,编程开发者的网络收藏夹
学习永远不晚

关于TypeScript声明合并

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

关于TypeScript声明合并

介绍

TypeScript中有些独特的概念可以在类型层面上描述JavaScript对象的模型。

这其中尤其独特的一个例子是“声明合并”的概念。 理解了这个概念,将有助于操作现有的JavaScript代码。 同时,也会有助于理解更多高级抽象的概念。

对本文件来讲,“声明合并”是指编译器将针对同一个名字的两个独立声明合并为单一声明。 合并后的声明同时拥有原先两个声明的特性。 任何数量的声明都可被合并;不局限于两个声明。

基础概念

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

let box: Box = {height: 5, width: 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, haveMuscles is not visible here
    }
}

因为haveMuscles并没有导出,只有animalsHaveMuscles函数共享了原始未合并的命名空间可以访问这个变量。

doAnimalsHaveMuscles函数虽是合并命名空间的一部分,但是访问不了未导出的成员。

命名空间与类和函数和枚举类型合并

命名空间可以与其它类型的声明进行合并。 只要命名空间的定义符合将要合并类型的定义。合并结果包含两者的声明类型。 TypeScript使用这个功能去实现一些JavaScript里的设计模式。

合并命名空间和类

这让我们可以表示内部类。

class Album {
    label: Album.AlbumLabel;
}
namespace Album {
    export class AlbumLabel { }
}

合并规则与上面合并命名空间小节里讲的规则一致,我们必须导出AlbumLabel类,好让合并的类能访问。 合并结果是一个类并带有一个内部类。 你也可以使用命名空间为类增加一些静态属性。

除了内部类的模式,你在JavaScript里,创建一个函数稍后扩展它增加一些属性也是很常见的。 TypeScript使用声明合并来达到这个目的并保证类型安全。

function buildLabel(name: string): string {
    return buildLabel.prefix + name + buildLabel.suffix;
}

namespace buildLabel {
    export let suffix = "";
    export let prefix = "Hello, ";
}

alert(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的混入。

模块扩展

虽然JavaScript不支持合并,但你可以为导入的对象打补丁以更新它们。让我们考察一下这个示例:

// observable.js
export class Observable<T> {
    // ... implementation left as an exercise for the reader ...
}

// map.js
import { Observable } from "./observable";
Observable.prototype.map = function (f) {
    // ... another exercise for the reader
}

它也可以很好地工作在TypeScript中, 但编译器对 Observable.prototype.map一无所知。 你可以使用扩展模块来将它告诉编译器:

// observable.ts stays the same
// map.ts
import { Observable } from "./observable";
declare module "./observable" {
    interface Observable<T> {
        map<U>(f: (x: T) => U): Observable<U>;
    }
}
Observable.prototype.map = function (f) {
    // ... another exercise for the reader
}


// consumer.ts
import { Observable } from "./observable";
import "./map";
let o: Observable<number>;
o.map(x => x.toFixed());

模块名的解析和用import/export解析模块标识符的方式是一致的。 更多信息请参考 Modules。 当这些声明在扩展中合并时,就好像在原始位置被声明了一样。

但是,你不能在扩展中声明新的顶级声明-仅可以扩展模块中已经存在的声明。

全局扩展

你也以在模块内部添加声明到全局作用域中。

// observable.ts
export class Observable<T> {
    // ... still no implementation ...
}

declare global {
    interface Array<T> {
        toObservable(): Observable<T>;
    }
}

Array.prototype.toObservable = function () {
    // ...
}

全局扩展与模块扩展的行为和限制是相同的。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

关于TypeScript声明合并

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

TypeScript声明合并如何实现

今天小编给大家分享一下TypeScript声明合并如何实现的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。1.接口合并inte
2023-07-02

TS中最常见的声明合并有哪些

小编给大家分享一下TS中最常见的声明合并有哪些,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!前言:在TS中最常见的声明合并:接口合并在聊接口合并之前,我们先来聊聊
2023-06-22

C++ Cartographer源码中关于MapBuilder的声明与构造

这篇文章主要介绍了C++ Cartographer源码中关于MapBuilder的声明与构造,前面已经谈到了Cartographer中添加轨迹的方法和传感器的数据流动走向。在添加轨迹的时候,除了添加位姿估计器还有采样器,订阅回调函数之外,最重要的是通过map_builder_bridge添加了一条轨迹
2023-05-13

基于JS实现将JSON数据转换为TypeScript类型声明的工具

在TypeScript 项目中,我们经常需要使用声明一系列的ts类型。然而,手动写的效率实在太低,本文就实现一个工具将 JSON 数据转换为 TypeScript 类型定义,需要的可以参考一下
2023-05-14

基于JS怎么将JSON数据转换为TypeScript类型声明的工具

这篇文章主要介绍“基于JS怎么将JSON数据转换为TypeScript类型声明的工具”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“基于JS怎么将JSON数据转换为TypeScript类型声明的工具”
2023-07-05

关于9月份PMP®考试成绩质询的声明

  根据中国国际人才交流基金会网站发布的通知,自上周公布9月两场PMI ®认证考试成绩之后,PMI ®(中国)收到部分考生对本次考试成绩的质询,现发布关于9月份考试成绩质询的声明(转发),供大家参考,更多PMP ®考试的相关资讯,可关注编程学习网网站的更新。  关于9月份考试成绩质询的声明(转发) 
关于9月份PMP®考试成绩质询的声明
2024-04-18

关于elementUi表格合并行数据并展示序号

这篇文章主要介绍了关于elementUi表格合并行数据并展示序号,通过给table传入span-method方法可以实现合并行或列,方法的参数是一个对象,感兴趣的朋友可以学习一下
2023-05-17

关于文件合并与修改md5值的问题

这篇文章主要介绍了关于文件合并与修改md5值的问题,使用本博客的方法,不仅仅可以修改md5值,还可以达到隐藏文件的目的,需要的朋友可以参考下
2023-05-15

浙江关于2019下半年软考电子合格证明下载打印的通知

  江软考办发布了关于2019下半年软考电子合格证明下载打印的通知,即日起2019年下半年软考合格考生可在浙江政务服务网下载打印电子合格证明。  关于2019年下半年全国计算机技术与软件专业技术资格(水平)考试电子合格证明下载打印的通知  各位考生:  即日起我省2019年下半年全国计算机技术与软件专业技术资格(水平)
浙江关于2019下半年软考电子合格证明下载打印的通知
2024-04-18

宁波关于2019年下半年软考电子合格证明下载打印的通知

  宁波市计算机软件考试办公室发布了《关于2019年下半年软考电子合格证明下载打印的通知》,即日起合格考生可下载打印电子合格证明。  关于2019年下半年软考电子合格证明下载打印的通知  各位考生:  即日起我市2019年下半年全国计算机技术与软件专业技术资格(水平)考试合格考生可在浙江政务服务网下载打印电子合格证明。
宁波关于2019年下半年软考电子合格证明下载打印的通知
2024-04-18

宁波关于2017年下半年软考电子合格证明下载打印的通知

  很多考生都在关注2017年下半年软考证书的领取,宁波软考办近日发布了《关于2017年下半年软考电子合格证明下载打印的通知》,具体内容如下:  各位考生:  即日起我市2017年下半年全国计算机技术与软件专业技术资格(水平)考试合格考生可在浙江政务服务网下载打印电子合格证明。查询打印网址:http://www.zjz
宁波关于2017年下半年软考电子合格证明下载打印的通知
2024-04-18

编程热搜

目录