typescript声明合并
RadiumAg 人气:0声明合并
类型合并
表明编译器
将合并
两个分开
的并且名称
相同的声明,合并
之后的声明
拥有两个声明
的特点,任意数量的声明
可以被合并,不仅限两个
。
合并Interface
1.interface
的非函数成员应该是唯一的,如果两个interface
都声明一个名称相同但类型不同
的非函数成员
,编译器
将提示错误:
interface Box { height: number; } interface Box { height: string; }
2.对于函数成员
,每个相同名称
的成员被看作是相同名称函数的重载
,但是当出现两个interface
时,第二个有更高的优先
级,会覆盖前一个:
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; }
当然这个规则有一个例外,当函数的参数类型是一个单字面量类型
(single string literal type),它将会根据优先级排序
,并放在声明顶部
:
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; }
合并Namespace
- 合并两个相同名称的
namespace
时,将进一步添加第二个namespace
中导出的成员
到第一个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 {} }
- 当一个
namespace
发生合并时,和它合并的namesapce
不能访问它的未导出的成员
:
namespace Animal { const haveMuscles = true; export function animalsHaveMuscles() { return haveMuscles; } } namespace Animal { export function doAnimalsHaveMuscles() { return haveMuscles; // Error, because haveMuscles is not accessible here } }
可以看到无法访问haveMuscles
,同时运行也会报错,可以结合编译后的例子看:
namespace和class、enum、function合并
- 和合并
namespace
一样,class
可以访问namespace
中导出的类型
和值
:
class Album { label: Album.AlbumLabel; } namespace Album { export class AlbumLabel {} }
namespace
和function
合并可以像javascript
那样在方法上添加属性:
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'));
可以看编译之后的代码,可以看到直接在buildLabel
上添加了属性:
namespace
和enum
发生合并时,namespace
可以扩展enum
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; } } }
可以看编译之后的:
class之间不允许合并,但是如果需要模仿类似功能,可以参照 Mixins in Typscripts
Module扩展
尽管Module
之间是不支持合并的,但是你可以通过导入需要扩展
的方法,然后再更改
它,这种方式去实现:
// 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 };
但是这样编译器并不能提供良好的提示,所以需要扩展module
的声明:
// 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());
全局扩展
如果在模块中,也可以在全局声明中
来扩展:
// observable.ts export class Observable<T> { // ... still no implementation ... } // 在这里扩展 declare global { interface Array<T> { toObservable(): Observable<T>; } } Array.prototype.toObservable = function () { // ... };
加载全部内容