在之前的函数章节中,通过声明多个函数类型实现函数的重载。函数重载就是函数声明合并。除了函数声明合并外。ts还会将接口和枚举声明进行合并。
接口声明合并
如果在同一模块中,声明多个相同的接口,接口会自动合并,并且保持属性:
interface User {
name: string;
}
interface User {
age: number;
}
// 等价于
interface User {
name: string;
age: number;
}
如果多个声明之间,有相同的属性(非函数),并且属性类型相同,则会报错;如果是函数属性,则会像函数重载合并该属性:
interface User {
name: string;
}
// 属性类型相同
interface User {
name: string;
}
// 属性不类型相同,报错
interface User {
name: number;
}
// 函数重载
interface Cloner {
clone(animal: Animal): Animal;
}
interface Cloner {
clone(animal: Sheep): Sheep;
}
interface Cloner {
clone(animal: Sheep): Sheep;
clone(animal: Animal): Animal;
}
接口的函数成员在重载时,在同一个声明中的顺序保持不变;但后声明的函数顺序会在先声明的函数之上,即后声明函数的有更高的优先级。(如上面合并结果是clone(animal: Sheep): Sheep
具备更高的优先级)
有一个例外,即函数参数中有一个参数是单个字符串字面量类型时,它会被冒泡到最顶部:
interface Document {
createElement(tagName: any): any;
}
interface Document {
createElement(tagName: "div"): any;
createElement(tagName: "span"): any;
}
interface Document {
createElement(tagName: string): any;
createElement(tagName: "canvas"): any;
}
// 相当于
interface Document {
createElement(tagName: "div"): any;
createElement(tagName: "span"): any;
createElement(tagName: "canvas"): any;
createElement(tagName: string): any;
createElement(tagName: any): any;
}
如果我们使用泛型接口,则泛型接口需要按照完全一样的方式声明,包括泛型名字;泛型约束也需要相同(如果部分声明使用约束,部分没有,则没有约束的声明默认使用已声明的约束):
interface User<T> {
name: T;
}
// 泛型名字不同,报错
interface User<U> {
name: U;
}
// 泛型约束相互不兼容,报错
interface User<T extends number> {
name: T;
}
interface User<T extends string> {
age: T;
}
// 泛型约束相同,所有的接口声明都继承于number
interface User<T extends number> {
name: T;
}
interface User<T> {
age: T;
}
虽然类型别名和接口大部分功能相似,但类型别名不具有声明合并,如果在同一个模块声明多个同名类型别名,ts会直接报错
枚举声明合并
枚举的声明合并与接口相似,但更为简单:
enum WeekDay {
Mon,
Tue,
Wed,
}
enum WeekDay {
Thu = 3, // 需要显式赋值
Fri,
}
// 等价于
enum WeekDay {
Mon,
Tue,
Wed,
Thu,
Fri,
}
合并多个枚举声明时,每个后续声明的第一个成员都需要声明初始值(至少需要为第一个成员赋值,也可以为每一个成员显式赋值)。
比较好的实践是为枚举的成员的每一项显式赋值,保证相互不覆盖。