sourceinsight找不到函数声明_typescript 12 声明合并

dc97bb5465efffa899970b441cdd943e.png

知识导向

一、基础概念

二、合并接口

三、合并命名空间

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

  1. 命名空间和类
  2. 命名空间和函数
  3. 命名空间和枚举

一、基础概念

名字相同多个申明合并为一个申明,合并后的申明同时拥有多个申明的特性。

TypeScript中的声明会创建以下三种实体之一:命名空间,类型或值。 创建命名空间的声明会新建一个命名空间,它包含了用(.)符号来访问时使用的名字。 创建类型的声明是:用声明的模型创建一个类型并绑定到给定的名字上。 最后,创建值的声明会创建在JavaScript输出中看到的值。

31e31b75107e6b3cabc89e67de4c6a02.png
理解每个声明创建了什么,有助于理解当声明合并时有哪些东西被合并了。

二、合并接口

最简单也最常见的声明合并类型是接口合并。 从根本上说,合并的机制是把双方的成员放到一个同名的接口里。

interface InfoInter {
    name: string
}
interface InfoInter {
    age: number
}

let info: InfoInter = {
    name: 'cyang',
    age: 18
}

接口的非函数的成员应该是唯一的。如果它们不是唯一的,那么它们必须是相同的类型。如果两个接口中同时声明了同名的非函数成员且它们的类型不同,则编译器会报错。

对于函数成员,每个同名函数声明都会被当成这个函数的一个重载。 同时需要注意,当接口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;
}

三、合并命名空间

与接口相似,同名的命名空间也会合并其成员。 命名空间会创建出命名空间和值,我们需要知道这两者都是怎么合并的。

对于命名空间的合并,模块导出的同名接口进行合并,构成单一命名空间内含合并后的接口。

对于命名空间里值的合并,如果当前已经存在给定名字的命名空间,那么后来的命名空间的导出成员会被加到已经存在的那个模块里。

例如:

namespace Validation {
    export const checkNumber = () => { }
}

namespace Validation {
    export const checkLetter = () => { }
}

等同于:

namespace Validation {
    export const checkNumber = () => { }
    export const checkLetter = () => { }
}

除了这些合并外,你还需要了解非导出成员是如何处理的。 非导出成员仅在其原有的(合并前的)命名空间内可见。这就是说合并之后,从其它命名空间合并进来的成员无法访问非导出成员。

namespace Validation {
    const numberReg = /^[0-9]$/
    export const checkNumber = () => { }
}

namespace Validation {
    export const checkLetter = () => { }
    console.log(numberReg); //Error找不到名称numberReg
}

因为numberReg并没有导出,只有checkNumber 函数共享了原始未合并的命名空间可以访问这个变量。checkLetter函数虽是合并命名空间的一部分,但是访问不了未导出的成员。

四、不同类型的合并

  1. 合并命名空间和类

这让我们可以表示内部类

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

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

2. 合并命名空间和函数

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

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

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

console.log(buildLabel("Sam Smith"));

3. 合并命名空间和枚举

enum Colors {
    red,
    green,
    blue,
}

namespace Colors {
    export const yellow = 3
}

console.log(Colors);
// 结果 {0: "red", 1: "green", 2: "blue", red: 0, green: 1, blue: 2, yellow: 3}
你会发现这里反向映射得到的 key的值为 0,1,2并没有3。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值