跳到主要内容

TypeScript 声明合并

TypeScript 编译器会将程序中多个具有相同名称的声明合并为一个声明。

但这并不是说 TypeScript 会随意的合并两个名称相同的字符串变量,这显然是不符合语法规定的,那么本节将介绍什么样的声明可以进行合并。

1. 慕课解释

TypeScript 中的声明会创建以下三种实体之一:命名空间、类型或值。

来看以下声明都创建了什么实体:

声明类型创建了命名空间创建了类型创建了值
Namespace
Class
Enum
Interface
Type Alias
Function
Variable

2. 合并接口

最简单也最常见的声明合并类型是接口合并。

interface Box {
height: number
width: number
}

interface Box {
scale: number
width: number // 类型相同 OK
}

let box: Box = {height: 5, width: 6, scale: 10}

接口合并,则接口的非函数的成员须是唯一的,哪怕不唯一,最起码也要类型相同。但如果类型不同,则编辑器报错。

对于函数成员,每个同名函数声明都会被当成这个函数的一个重载,后面的接口具有更高优先级。

接口合并时,将遵循以下规范:

  • 接口内优先级是从上到下;
  • 后面的接口具有更高优先级;
  • 如果函数的参数是字符串字面量,会被提升到函数声明的最顶端。
interface Document {
createElement(tagName: any): Element // 5
}
interface Document {
createElement(tagName: 'div'): HTMLDivElement // 2
createElement(tagName: 'span'): HTMLSpanElement // 3
}
interface Document {
createElement(tagName: string): HTMLElement // 4
createElement(tagName: 'canvas'): HTMLCanvasElement // 1
}

按照上面介绍的规则,得到合并后的声明:

interface Document {
createElement(tagName: 'canvas'): HTMLCanvasElement
createElement(tagName: 'div'): HTMLDivElement
createElement(tagName: 'span'): HTMLSpanElement
createElement(tagName: string): HTMLElement
createElement(tagName: any): Element
}

3. 合并命名空间

合并多个具有相同名称的命名空间:

  • 导出成员不可重复定义
  • 非导出成员仅在其原有的(合并前的)命名空间内可见
namespace A {
let used = true

export function fn() {
return used
}
}

namespace A {
export function fnOther() {
return used // Error, 未找到变量 used
}
}

A.fn() // OK
A.fnOther() // OK

代码解释:

第一个命名空间内的非导出成员 used 仅在第一个命名空间内可见。 命名空间对象 A 可以分别访问在第一个或第二个声明的导出成员。

4. 命名空间与其它类型的合并

只要命名空间的定义符合将要合并类型的定义,命名空间就可以与其它类型的声明进行合并,合并结果包含两者的声明类型。

4.1 命名空间与类的合并

合并名称相同的命名空间与类:

  • 命名空间内的成员必须导出,合并后的类才能访问
  • 命名空间内导出的成员,相当于合并后类的静态属性
  • 命名空间要放在类的定义后面