TypeScript学习(十二):声明合并

837 阅读9分钟

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 (){
    // ...
}

参考

www.typescriptlang.org/docs/handbo…