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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/23217334/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-11 01:14:26  来源:igfitidea点击:

How do I extend a TypeScript class definition in a separate definition file?

typescript

提问by

I have a JS library called leaflet which has an existing TypeScript definition file.

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

I wish to use a plugin which extends some of the objects in leaflet with an extra function.

我希望使用一个插件来扩展传单中的一些对象,并具有额外的功能。

In the existing TypeScript definition file the objects are defined as classes rather than interfaces.

在现有的 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;
    }
}

If I try and define it a second time in a separate file then I get an error about "Duplicate Identifier 'CircleMarker'.".

如果我尝试在单独的文件中再次定义它,则会收到有关“重复标识符‘CircleMarker’。”的错误消息。

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

This makes sense as it's a class and not an interface, but that being the case is there a way to extend this class definition without changing the original definition file?

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

The base definition file is pulled in from DefinitelyTyped via nuget so I have a very strong desire not to make any changes to it as it'll make updating much more awkward/prone to failure.

基本定义文件是通过 nuget 从绝对类型中提取的,因此我非常希望不要对其进行任何更改,因为它会使更新变得更加笨拙/容易失败。

采纳答案by WiredPrairie

If you don't control the original definition file, and can't make adjustments to it, then unfortunately, what you're trying to do isn't supported currently in TypeScript. An interfacein TypeScript is the only construct that allows reasonable extensions as it is only a compile-time/syntax check and not a run-time operation.

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

You cannot extend a classin TypeScript with new functionality using only TypeScript (and expecting code-completion/Intellisense to work as expected). You could of course add the functions to the prototypefor the CircleMarkerclass, but they would be unavailable to Intellisense and would fail to compile unless you use a type assertion.

您不能class仅使用 TypeScript 使用新功能扩展TypeScript 中的 a(并期望代码完成/智能感知按预期工作)。你当然可以添加功能,到prototypeCircleMarker类,但他们将无法使用智能感知,并会出错,除非你使用一个类型断言。

Instead of using any, you should be able to use an interfacewith the type assertion:

any您应该能够将 aninterface与类型断言一起使用,而不是使用:

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

Then:

然后:

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

Thankfully, it doesn't add any run-time overhead, just a bit of extra typing (pun intended!).

值得庆幸的是,它没有增加任何运行时开销,只是一些额外的输入(双关语!)。

There have been suggestions for things like "mix-ins"on CodePlex, but they have not been implemented. Even the mix-in suggestions would not be entirely straightforward to use, and wouldn't work well for libraries that weren't entirely written in TypeScript (as it would be too easy to have JavaScript code that simply could not be safely constructed for example with a mix-in).

在 CodePlex 上有人提出了诸如“混入”之类的建议,但尚未实施。即使是混合建议也不是完全直接使用,并且对于不是完全用 TypeScript 编写的库也不能很好地工作(因为 JavaScript 代码太容易无法安全构建,例如混入)。

回答by basarat

You can't do that with the classkeyword. There is a feature request you can vote on here : https://typescript.codeplex.com/workitem/917

你不能用class关键字来做到这一点。您可以在此处投票的功能请求:https: //typescript.codeplex.com/workitem/917

You can however mimic classes using interfacesas shown in the workaround (https://typescript.codeplex.com/workitem/917) for the issue. In your case

但是,您可以使用该问题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;
    }
}

and extend it

并扩展它

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

回答by Roy Yin

This is what I tried and it feels relatively comfortable for me

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

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

where CircleMarkerExand excan be defined as

其中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;
}

then

然后

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

but it is still a little strange

但还是有点奇怪

回答by Rune Jeppesen

Is this possible?

这可能吗?

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

And then define your CircleMarker instances as MyCircleMarker

然后将您的 CircleMarker 实例定义为 MyCircleMarker

回答by tanguy_k

Edit: this answer is off topic. It talks about interfaces and not classes like asked

编辑:这个答案是题外话。它谈论接口而不是像被问到的类

I didn't have any problem extending a TypeScript (v3.6) external type definition.

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

Example with google.maps.Marker type definition:

google.maps.Marker 类型定义示例:

// 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;
  }
}

 

 

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

You don't have to modify your tsconfig.json: it just works.

您不必修改您的 tsconfig.json:它只是有效。

It's the way DefinitelyTyped works

这是绝对类型的工作方式

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

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

declare module 'webpack' {
  interface Configuration {
    devServer?: WebpackDevServer.Configuration;
  }
}

webpack-dev-server type definition extends webpack type definition. If you use @types/webpack and not @types/webpack-dev-server you won't have the devServerproperty.

webpack-dev-server 类型定义扩展了 webpack 类型定义。如果您使用 @types/webpack 而不是 @types/webpack-dev-server 您将没有该devServer属性。

Another example:

另一个例子:

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

 

 

global.hello = 'Hello, World!';