TypeScript系列 进阶篇:(三) 声明合并

4,106 阅读4分钟

TypeScript系列 进阶篇:(三) 声明合并

声明合并是将两个或两个以上的独立声明合并为一个声明,这个合并声明会拥有所有之前每个声明的特性。

[toc]

一、基础概念

在TS中,一个声明会创建一个实体,为以下三种之一:

  • 命名空间:使用 namespace 来声明命名空间,命名空间的声明会创建一个命名空间和值,至于什么是命名空间,会在后续的文章中介绍;

  • 类型:类型的声明一般使用 interfacetype 关键字,得到一个类型,此外, classenum 关键字的声明也会得到一个类型;

  • :通常使用 var , let , const 来声明一个变量,创建一个值,此外,classenumfunction关键字也会声明一个值。

可以看出,命名空间的声明,创建一个命名空间和一个值;变量的声明只会创建一个值;interface 和 type 的声明只会创建一个类型;classenum 的声明,既会创建一个值,也会创建一个同名的类型

二、合并 interface 接口

interface的合并是最简单、最常见的声明合并。主要声明多个同名的 interface ,TS就会自动将其合并。但是这些 interface 中,如果有同名的非函数字段,则该字段对应的类型也应该相同,否则会被标记为错误。

interface A {
  name: string,
  age: number,
}

interface A {
  // 同名字段name,对应的类型也相同,
  name: string,
  gender: 1 | 2,
}

interface B {
  name: string,
  age: number,
}

interface B {
  // 报错,同名字段age,对应的类型不同,分别为number和string
  age: string,
  gender: 1 | 2,
}

而在interface的合并中,同名的函数则会被视为同一函数的重载,且后声明的接口具有更高的优先级。

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

// 会被合并为如下声明
interface Document {
  createElement(tagName: "canvas"): HTMLCanvasElement;
  createElement(tagName: "div"): HTMLDivElement;
  createElement(tagName: "span"): HTMLSpanElement;
  createElement(tagName: string): HTMLElement;
  createElement(tagName: any): Element;
}

三、合并 namespace 命名空间

和 interface 的合并类似,声明的同名namespace也会合并每个声明的成员。由于namespace会创建namespace和值,命名空间和值都会合并。

// 声明两个独立的命名空间
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 的声明中,没有被 export 的成员,无法在另一个同名的namespace中获取,也无法使用namespace的名字来访问

// name 没有被 export 导出
namespace Person {
  let name = "cc"
  export function getName(){
    return name
  }
}

namespace Person {
  export function setName(str: string){
    // 错误,无法获取
    name = str
  }
}

在上面的栗子中,由于在第一个namespace中,name没有被导出,因此在第二个namespace中,无法获取到name。只需要经过 export ,就可以在其它同名的namespace中共享该成员了。

// name 没有被 export 导出
namespace Person {
  export let name = "cc"
  export function getName(){
    return name
  }
}

namespace Person {
  export function setName(str: string){
    name = str
  }
}

四、将 NamespacesClassesFunctionsEnums 合并

1. 合并命名空间与类

在命名空间中导出的成员,会成为同名的class的静态成员,而没有导出的成员,则无法在被class获取。

class Album {
  label: Album.AlbumLabel;
}
namespace Album {
  export class AlbumLabel {};
  export const name = "cc";
  let age = 18;
}

Album.name  // "cc"
Album.age  // 报错

const album = new Album()
album.name  // 报错,没有实例

2. 合并 命名空间 与 函数

命名空间中导出的成员,会成为同名函数的属性。

function logName(str: string){
  console.log(str + logName.name)
}

namespace logName {
  export const name = "cc"
  export const age = 18
  let gender = 1
}

logName("yy")

3. 合并 命名空间 和 枚举

合并命名空间和枚举,会将命名空间导出的成员作为枚举的扩充。

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;
    }
  }
  export const pink = 10
}

Color.mixColor("yellow")  // 3
Color.pink  // 10

另外还有模块扩充和全局扩充,在这里就不介绍了。下一篇,就讲讲 枚举 Enums 吧。