如何在单独的定义文件中扩展TypeScript类定义?

时间:2014-04-22 10:47:54

标签: typescript

我有一个名为leaflet的JS库,它有一个现有的TypeScript定义文件。

我希望使用一个插件,它可以使用额外的函数扩展传单中的一些对象。

在现有的TypeScript定义文件中,对象被定义为类而不是接口。

e.g。

declare module L {
    function circleMarker(latlng: LatLng, options?: PathOptions): CircleMarker;

    export class CircleMarker extends Circle {
        constructor(latlng: LatLng, options?: PathOptions);
        setLatLng(latlng: LatLng): CircleMarker;
        setRadius(radius: number): CircleMarker;
        toGeoJSON(): any;
    }
}

如果我尝试在单独的文件中再次定义它,那么我会收到关于"重复标识符' CircleMarker'。"。

的错误。
declare module L {
    export class CircleMarker {
        bindLabel(name: string, options: any): CircleMarker;
    }
}

这是有道理的,因为它是一个类而不是一个接口,但是在这种情况下是否有办法在不更改原始定义文件的情况下扩展此类定义?

基本定义文件是通过nuget从DefinitelyTyped中提取的,因此我非常强烈地希望不对其进行任何更改,因为它会使更新变得更加笨拙/容易出现故障。

6 个答案:

答案 0 :(得分:23)

如果您无法控制原始定义文件,并且无法对其进行调整,那么很遗憾,目前TypeScript中不支持您尝试执行的操作。 TypeScript中的interface是唯一允许合理扩展的结构,因为它只是编译时/语法检查而不是运行时操作。

使用仅使用TypeScript的新功能(并期望代码完成/智能感知按预期工作),您无法在TypeScript中扩展class。您当然可以将函数添加到prototype类的CircleMarker,但它们将不可用于Intellisense,并且除非您使用类型断言,否则将无法编译。

您应该能够使用类型断言的any,而不是使用interface

declare module L {
    export interface CircleMarkerEx {
        bindLabel(name: string, options: any): CircleMarker;
    }
}

然后:

var cm = <L.CircleMakerEx> circle.bindLabel("name", {});

值得庆幸的是,它不会增加任何运行时开销,只需要额外输入(双关语!)。

在CodePlex上有类似"mix-ins"之类的建议,但它们尚未实施。即使是混合使用的建议也不会完全直接使用,并且对于那些完全没有用TypeScript编写的库来说也不会很好用(因为很容易找到根本无法使用的JavaScript代码)例如用混合物安全地构造。

答案 1 :(得分:13)

您无法使用class关键字执行此操作。您可以在此处投票选择功能:https://typescript.codeplex.com/workitem/917

但是,您可以使用interfaces模拟类,如解决方法(https://typescript.codeplex.com/workitem/917)中所示。在你的情况下

declare module L {
    function circleMarker(latlng: LatLng, options?: PathOptions): CircleMarker;

    declare var CircleMarker: CircleMarkerStatic;
    export interface CircleMarkerStatic{
      new (latlng: LatLng, options?: PathOptions): CircleMarker;
    }

    export interface CircleMarker {
        setLatLng(latlng: LatLng): CircleMarker;
        setRadius(radius: number): CircleMarker;
        toGeoJSON(): any;
    }
}

并扩展它

declare module L {
    export interface CircleMarker {
        bindLabel(name: string, options: any): CircleMarker;
    }
}

答案 2 :(得分:3)

这是我尝试过的,对我来说感觉相对舒服

declare module L {
    export class CircleMarkerEx {
        constructor(source: CircleMarker);
        public bindLabel(name: string, options: any): CircleMarker;
    }
    export function ex(cm: CircleMarker): CircleMarkerEx;
}

其中CircleMarkerExex可以定义为

class CircleMarkerExtender extends CircleMarker {    
    public b() {
        return `${this.a()} extended`;
    }
}
CircleMarker.prototype = Object.create(CircleMarkerExtender.prototype);
CircleMarker.prototype.constructor = CircleMarker;

export function ex(cm: CircleMarker) {
    return cm as CircleMarkerExtender;
}

然后

let cm = ex(circle).bindLabel("name", {});

但它仍然有点奇怪

答案 3 :(得分:2)

这可能吗?

declare module L {
    export class MyCircleMarker extends CircleMarker{
        bindLabel(name: string, options: any): CircleMarker;
    }
}

然后将CircleMarker实例定义为MyCircleMarker

答案 4 :(得分:0)

编辑:此答案不在主题范围内。它谈论的是接口,而不是诸如Asked这样的类

扩展TypeScript(v3.6)外部类型定义没有任何问题。

带有google.maps.Marker type definition的示例:

request.AddFile("file", "http://azure.blob/testblob/test.txt");    

// File types/googlemaps-marker-hello.d.ts or
// whatever file name you like, it doesn't matter
declare namespace google.maps {
  interface Marker {
    hello: string;
  }
}

您不必修改tsconfig.json:它可以正常工作。

这是DefinitelyTyped工作的方式

请参见https://github.com/DefinitelyTyped/DefinitelyTyped/blob/edb4917e1bbd0d860b51e7806775cb505f858e36/types/webpack-dev-server/index.d.ts#L206-L211

// Later on inside src/MyCode.ts
const marker = new google.maps.Marker();
marker.hello = 'Hello, World!';

webpack-dev-server类型定义扩展了webpack类型定义。如果您使用@ types / webpack而不是@ types / webpack-dev-server,那么您将没有declare module 'webpack' { interface Configuration { devServer?: WebpackDevServer.Configuration; } } 属性。

另一个例子:

devServer

declare namespace NodeJS {
  interface Global {
    hello: string;
  }
}

答案 5 :(得分:0)

对于那些正在寻找新的/当前记录在案的解决方案的人,可以在以下位置找到:

https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation

我用它来扩展 google-protobuf 生成的类的原型,以添加额外的谓词。

import { CgMessage, CgType } from "./CgProto";
declare module './CgProto' {
  interface CgMessage {
    expectsAck(): boolean 
  }
}
CgMessage.prototype.expectsAck = function() {
  return [CgType.send, CgType.join, CgType.leave].includes(this.type)
}