最近在进行前端开发的时候,为了拓展引用,使用了TS声明合并的特性,完整的了解了一下该特性,特此记录成笔记。
声明合并指在TypeScript里面,编译器将两个或者多个独立的相同名称的声明合并到一起。合并后的定义同时包含所有声明。可以理解为声明的拓展
接口合并
最常见的合并就是接口合并,接口合并后的成员会整合到一起。
- 接口中的非函数成员应该是唯一的,如果不是唯一,那他们必须有相同的类型,如果声明了两个同名但是类型不通的非函数成员会报错
- 对于函数成员而言,每一个同名函数成员都相当于当前函数成员的一个版本,第一个接口A和第二个接口A合并的时候,第二个接口声明对象的优先级会高于第一个
eg:
1 2 3 4 5 6 7 8 9 10
| interface Cloner { clone(animal: Animal): Animal; } interface Cloner { clone(animal: Sheep): Sheep; } interface Cloner { clone(animal: Dog): Dog; clone(animal: Cat): Cat; }
|
合并后:
1 2 3 4 5 6
| interface Cloner { clone(animal: Dog): Dog; clone(animal: Cat): Cat; clone(animal: Sheep): Sheep; clone(animal: Animal): Animal; }
|
- 当函数参数是单个字符串字面量类型时,合并后将被“提升”至重载列表顶部
eg:
1 2 3 4 5 6 7 8 9 10 11
| 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; }
|
合并后:
1 2 3 4 5 6 7
| interface Document { createElement(tagName: "canvas"): HTMLCanvasElement; createElement(tagName: "div"): HTMLDivElement; createElement(tagName: "span"): HTMLSpanElement; createElement(tagName: string): HTMLElement; createElement(tagName: any): Element; }
|
合并命名控件
和接口合并类似,合并命名空间的时候也会合并其成员
- 合并命名空间的时候,每个命名空间中声明的导出接口的类型定义会自行进行合并,从而形成一个包含合并后的接口定义的单一命名空间。未导出的类型不会合并
eg:
1 2 3 4 5 6 7 8 9
| namespace Animals { export class Zebra {} } namespace Animals { export interface Legged { numberOfLegs: number; } export class Dog {} }
|
合并后:
1 2 3 4 5 6 7
| namespace Animals { export interface Legged { numberOfLegs: number; } export class Zebra {} export class Dog {} }
|
模块扩充
虽然 JavaScript 模块不支持合并操作,但可以通过导入并更新现有对象来对其进行修补,在使用第三方框架,框架引用了其他组件,但是第三方框架没有提供该组件足够的属性的时候,可以使用这种方式来拓展来避免更新整个框架。
eg:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| // 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 //通过拓展合并来使用户来调用Observable的map方法 import { Observable } from "./observable"; import "./map"; let o: Observable<number>; o.map((x) => x.toFixed());
|
eg2;
原本的框架里面,对
1 2 3 4 5
| export declare const Table: { tooltipOptions: import("vue").PropType<Partial<Pick<import("element-plus").ElTooltipProps, "showArrow" | "appendTo" | "transition" | "effect" | "enterable" | "popperClass" | "offset" | "placement" | "popperOptions" | "showAfter" | "hideAfter">>>; } export default Table; export type {TableProps}
|
这里Table是某框架封装的属性,使用了的element-plus组件,并提供了tooltipOptions部分属性,但是这里是不够的,实际使用的时候期望可以使用全部属性,又不想升级框架,这里就可以声明命名空间来进行拓展。
1 2 3 4 5 6 7 8 9 10 11 12
| // src/types/pure-table.d.ts import "element-plus"; import "/table";
// 声明合并 TableProps declare module "/table" { // 这里扩展 PureTableProps 的定义 interface PureTableProps { // 扩展 tooltipOptions 支持完整的 ElTooltipProps tooltipOptions?: Partial<import("element-plus").ElTooltipProps>; } }
|
合并之后,在使用Tabel组件的时候,其提供的tooltipOptions属性将支持完整的ElTooltipProps
命名空间与类、函数、枚举合并
目前Ts支持命名空间与其他类型的合并,需要注意的是类与类之间的合并是不允许的
命名空间和类
1 2 3 4 5 6
| class Album { label: Album.AlbumLabel; } namespace Album { export class AlbumLabel {} }
|
此时Album.label是合法的调用
命名空间和函数
1 2 3 4 5 6 7 8
| function buildLabel(name: string): string { return buildLabel.prefix + name + buildLabel.suffix; } namespace buildLabel { export let suffix = ""; export let prefix = "Hello, "; } console.log(buildLabel("Sam Smith"));
|
命名空间和枚举
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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; } } }
|