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 命名空间与类的合并
合并名称相同的命名空间与类:
- 命名空间内的成员必须导出,合并后的类才能访问
- 命名空间内导出的成员,相当于合并后类的静态属性
- 命名空间要放在类的定义后面
实例演示
预览
复制
class Album {
label!: Album.AlbumLabel
}
namespace Album {
export class AlbumLabel { }
export const num = 10
}
console.log(Album.num) // 10
运行案例
点击 "运行案例" 可查看在线运行效果
注意: 命名空间要放在类的定义后面,命名空间内导出的成员,相当于合并后类的静态属性。
4.2 命名空间与函数的合并
- 名称相同的命名空间与函数挂载同一个对象
- 命名空间要放在函数的定义后面
实例演示
预览
复制
function buildLabel(name: string): string {
return buildLabel.prefix + name + buildLabel.suffix
}
namespace buildLabel {
export let suffix = '.C'
export let prefix = 'Hello, '
}
console.log(buildLabel('Mr.Pioneer')) // Hello, Mr.Pioneer.C
运行案例
点击 "运行案例" 可查看在线运行效果
命名空间和函数可以进行合并,是因为在 JavaScript 中函数也是对象。
4.3 命名空间与枚举的合并
命名空间可以用来扩展枚举型:
实例演示
预览
复制
enum Color {
red = 1,
green = 2,
blue = 4
}
namespace Color {
export function mixColor(colorName: string) {
switch (colorName) {
case 'yellow':
return Color.red + Color.green
case 'white':
return Color.red + Color.green + Color.blue
default:
break
}
}
}
console.log(Color.mixColor('yellow')) // 3
运行案例
点击 "运行案例" 可查看在线运行效果
解释: 枚举本身也是个对象,与命名空间对象合并后对象的属性进行了扩充。
5. 小结
虽然 TypeScript 有声明合并的功能,但是在工作中应尽量避免定义命名相同的声明,合理使用模块来规避这类情况。
TypeScript 简介
TypeScript 简介与优势
TypeScript 安装与使用
TypeScript 基础
TypeScript 变量声明
TypeScript 基础类型
TypeScript BigInt
TypeScript Symbol
TypeScript 元组(Tuple)
TypeScript 枚举(Enum)
TypeScript Never & Unknow
TypeScript 接口(Interface)
TypeScript 类(Class)
TypeScript 函数(Function)
TypeScript 字面量类型
TypeScript 类型推断
TypeScript 类型断言
TypeScript 类型保护
TypeScript 进阶
TypeScript 泛型(Generic)
TypeScript 类型兼容性
TypeScript 交叉类型
TypeScript 联合类型
TypeScript 类型别名
TypeScript 索引类型
TypeScript 映射类型
TypeScript 条件类型
TypeScript is 关键字
TypeScript infer 关键字
TypeScript Truthy 与 Falsy
TypeScript 迭代器(Iterator)
TypeScript 生成器(Generator)
TypeScript 装饰器(Decorator)
TypeScriptReflectMetadata
TypeScript 混入(Mixins)
TypeScript 工程
TypeScript 模块
TypeScript 命名空间
TypeScript 声明合并
TypeScript 编译选项
TypeScript tsconfig.json
TypeScript TSLint与ESLint
代码预览
退出