介绍

指编译器将针对同一个名字的两个独立声明合并为单一声明,合并后的声明同时拥有原先两个声明的特性

注:任何数量的声明都可被合并

// 两个相同的接口会进行合并
interface Box {
  height: number;
  width: number;
}
interface Box {
  scale: number;
}
let box: Box = {height: 5, width: 6, scale: 10};
console.log(box)

TS的声明会创建以下三种实体之一:

  • 命名空间:会创建一个新的命名空间,他包含了用(.)符号来访问时使用的名字
  • 类型:用声明的模型创建一个类型并绑定到给定的名字上
  • 值:会创建在JS输出中看到的值

合并接口:最简单也是最常见的合并类型是接口合并,合并机制是:把双方的成员放到一个同名的接口里

注意事项:

  • 接口的非函数成员应该是唯一的,如果不是唯一的那么他们必须是相同的类型,否则会报错
  • 对于函数成员,每个同名函数声明都会被当成这个函数的一个重载,注:同名接口后面的接口具有更高的优先级
// 同名接口后面的接口具有更高的优先级
interface Animal {}
interface Sheep {}
interface Dog {}
interface Cat {}
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 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 clasas Dog { }
}
// 对于非导出成员
namespace Animal {
  let haveMuscles = true;

  export function animalsHaveMuscles() {
      return haveMuscles;
  }
}
// doAnimalsHaveMuscles函数虽是合并命名空间的一部分,但是访问不了未导出的成员
namespace Animal {
  export function doAnimalsHaveMuscles() {
      // return haveMuscles;  // Error, because haveMuscles is not accessible here
  }
}