Declaration merging in typescript

statement merge

类型合并Indicates 编译器that 合并two 分开and the 名称same statement, the 合并latter 声明has 声明the characteristics of two, any number of 声明can be combined, not only 两个.

Merge Interface

1. interfaceThe non-function members of 1. should be unique. If both interfacedeclare one 名称相同但类型不同, 非函数成员an 编译器error will be prompted:

interface Box { 
   height: number; 
 }
interface Box {
   height: string; 
 }
复制代码

image.png

2. For 函数成员, members of each 相同名称are seen as 相同名称函数的重载, but when there are two interface, the 第二个有更高的优先class, overrides the previous one:

interface Cloner {
  clone(animal: Animal): Animal;
}

interface Cloner {
  clone(animal: Sheep): Sheep;
}

interface Cloner {
  clone(animal: Dog): Dog;
  clone(animal: Cat): Cat;
}

// 最终的排序是
interface Cloner {
  clone(animal: Dog): Dog;
  clone(animal: Cat): Cat;
  clone(animal: Sheep): Sheep;
  clone(animal: Animal): Animal;
}
复制代码

Of course there is an exception to this rule, when the parameter type of the function is a 单字面量类型(single string literal type), it will be 优先级排序placed according to 声明顶部:

interface Document {
  createElement(tagName: any): Element;
}

interface Document {
  createElement(tagName: 'div'): HTMLDivElement;
  createElement(tagName: 'span'): HTMLSpanElement;
}

interface Document {
  createElement(tagName: string): HTMLElement;
  createElement(tagName: 'canvas'): HTMLCanvasElement;
}

// 字面量根据冒泡排序并放在了声明顶部
interface Document {
  createElement(tagName: 'canvas'): HTMLCanvasElement;
  createElement(tagName: 'div'): HTMLDivElement;
  createElement(tagName: 'span'): HTMLSpanElement;
  createElement(tagName: string): HTMLElement;
  createElement(tagName: any): Element;
}
复制代码

Merge Namespaces

  1. When merging two of the same name namespace, the second will be further added namespaceto 导出的成员the first namespace.
namespace Animals {
  export class Zebra {}
}

namespace Animals {
  export interface Legged {
    numberOfLegs: number;
  }
  export class Dog {}
}

// 合并到了第一个
namespace Animals {
  export interface Legged {
    numberOfLegs: number;
  }
  export class Zebra {}
  export class Dog {}
}
复制代码
  1. When a namespacemerge occurs, the merged with it namesapcecannot access it 未导出的成员:
namespace Animal {
  const haveMuscles = true;
  export function animalsHaveMuscles() {
    return haveMuscles;
  }
}
namespace Animal {
  export function doAnimalsHaveMuscles() {
    return haveMuscles; // Error, because haveMuscles is not accessible here
  }
}
复制代码

image.pngIt can be seen that it cannot be accessed haveMuscles, and an error will be reported when running at the same time. You can see it in conjunction with the compiled example:

image.png

namespace and class, enum, function merge

  1. As with merging namespace, the exported sum classcan be accessed in :namespace类型
class Album {
  label: Album.AlbumLabel;
}
namespace Album {
  export class AlbumLabel {}
}
复制代码
  1. namespaceand functionmerge can javascriptadd attributes on methods like this:
function buildLabel(name: string): string {
  return buildLabel.prefix + name + buildLabel.suffix;
}
namespace buildLabel {
  export const suffix = '';
  export const prefix = 'Hello, ';
}
console.log(buildLabel('Sam Smith'));
复制代码

You can look at the compiled code, and you can see that buildLabelthe attributes are added directly on:

image.png

  1. namespaceand enumwhen a merge occurs, namespaceit can be extendedenum
enum Color {
  red = 1,
  green = 2,
  blue = 4,
}

namespace Color {
  export function mixColor(colorName: string) {
    if (colorName == 'yellow') {
      return Color.red + Color.green;
    } else if (colorName == 'white') {
      return Color.red + Color.green + Color.blue;
    } else if (colorName == 'magenta') {
      return Color.red + Color.blue;
    } else if (colorName == 'cyan') {
      return Color.green + Color.blue;
    }
  }
}
复制代码

You can see after compilation:

image.png

Merging between classes is not allowed, but if you need to imitate similar functions, you can refer to Mixins in Typscripts

Module extension

Although Modulemerging between them is not supported, you can implement it by importing 扩展the required method and then using 更改it:

// observable.ts
export class Observable<T> {
  // ... implementation left as an exercise for the reader ...
}

// map.ts
import { Observable } from "./observable";
Observable.prototype.map = function (f) {
  // ... another exercise for the reader
};
复制代码

But this way the compiler doesn't provide good hints, so an extended moduledeclaration is required:

// observable.ts
export class Observable<T> {
  // ... implementation left as an exercise for the reader ...
}

// 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) {
  // ... another exercise for the reader
};

// consumer.ts
import { Observable } from "./observable";
import "./map";
let o: Observable<number>;
o.map((x) => x.toFixed());
复制代码

Global expansion

If it is in a module, it can also be 全局声明中extended here:

// observable.ts
export class Observable<T> {
  // ... still no implementation ...
}
// 在这里扩展
declare global {
  interface Array<T> {
    toObservable(): Observable<T>;
  }
}
Array.prototype.toObservable = function () {
  // ...
};
复制代码

ReferencesDeclaration Merging

Guess you like

Origin juejin.im/post/7143096757524643877