TS 会合并同名的多个类型声明至一个类型类型结构
TS 声明会创建出包含命名空间,类型与值的实体。命名空间创建声明会创建一个命名空间,可以通过 “.” 符号访问。类型创建声明会创建一个有特定“形状”的类型并绑定至特定名称。最后,值创建声明会创建在 JS 文件中可见的值。
理解每种声明创建了什么可以和帮助理解声明合并行为。
下图中表示的意思是 声明类型 a (同时)是 命名空间/类型/值中的一种或多种。
合并interface
对于非函数成员,相同名称的 interfere 会整合所有的成员,如果名称相同的 interface 的同名成员的类型不一样,TS 会报错。
interface Box{
height: number;
width: number
}
interface Box{
scale: number
}
let box: Box = {height: 5, width: 6, scale: 10}
interface a{
name: string
}
intreface a{
// crash
name: number
}
对于函数成员而言,由于同名函数会有不同的类型,所以会有重载的效果。后声明的函数拥有更高的优先级。
interface Cloner{
clone(animal: Animal): Animal
}
interface Cloner{
clone(animal: Sheep): Sheep
}
// 相当于
interface Cloner{
clone(animal: Sheep): Sheep;
clone(animal: Animal): Animal
}
但是,有一个特例,如果参数是一个单个字符串的类型,如 params: "a" ( 非字符串类型的联合类型, “a” | "b"),那么这个函数会冒泡到到上层去。
interface Document{
createElement(tagName: any): Element;
}
interface Document{
createElement(tagName: "div"): HTMLDivElement;
}
interface Document{
createElement(tagName: "div"|"a"): Element
}
//相当于
interface Document{
createElement(tagName: "div"): HTMLDivElement;
createElement(tagName: "div"|"a"):Element;
createElement(tagName: any):Element;
}
合并命名空间
合并命名空间:相同的命名空间中导出的 interface 会自动合并,合并规则如上,最后形成每个命名空间有一个合并后的 interface
命名空间值的合并:如果有两个相同的命名空间,第二个命名空间的导出成员会被加入第一个命名空间。(感觉这里,原文太啰嗦了,试了一下,两个相同的命名空间,其内部不同的导出成员,可以在任意命名空间内访问)
注意:一定要是 export member
namespace Animals{
export class Zebra{}
// work
let a:Legged
// crash
let c:Dog
}
namespace Animals{
export interface Legged{
numberOfLegs: number
}
class Dog{}
// work
let b:Zebra
}
命名空间的使用
利用“.”可以访问命名空间内 导出 的类型、值。
class Album{
label: Album.AmbulLabel
}
namespace Album{
export class AlbumLabel {}
}
function buildLabel(name: string): string{
return buildLabel.prefix + name + buildLable.suffix
}
namespace buildLabel{
export let suffix = "";
export let prefix = "Hello. ";
}
enum Color{
red = 1,
green = 2,
blue = 4,
}
namespace Color{
export fucntion mixColor(colorName: string){
if(colorName == "yellow"){
return Color.red + Color.green
}
}
}
不可合并部分
类 不能合并其他类或者变量。
更多内容可以参考 混入 。
模块扩充 Module Augmentation
JS 的模块不支持合并。但是可以通过导入并更新现有对象以完成类似“合并”
// observalb.ts
export class Observable<T>{
// ... implementation
}
// map.ts
import { Observable } from "./observable"
Observable.prototype.map = function (f){
// implement
}
declare module
注意下方的导入方法 import "./map",这种情况下,import 不做任何事情。但是 math.ts 内的代码会被指定,从而带来副作用( www.typescriptlang.org/docs/handbo…
// observable.ts
export class Observable<T>{
// ...
}
// map.ts
import { Observable } from "./observable";
declare module "./observable" {
interface Observable<T> {
map<U>(f: (X: T) => U): Observable<U>
}
}
Observable.prototype.map = function (f){
// ...
}
// consumer.ts
import { Observable } from "./observable";
import "./map";
let o: Observable<number>
o.map(x => x.toFixed())
全局扩充 Global Augmentation
export class Observable<T>{
// implement
}
declare global {
interface Array<T> {
toObservable(): Observable<T>
}
}
Array.prototype.toObservable = function (){
// ...
}