首页 文章

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

提问于
浏览
41

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

我希望使用一个插件,它通过一个额外的功能扩展了传单中的一些对象 .

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

例如

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中提取的,因此我非常强烈地希望不对其进行任何更改,因为它会使更新变得更加笨拙/容易出现故障 .

4 回答

  • 2

    如果你没有对它进行调整,那么很遗憾,你目前在TypeScript中支持的是什么 . TypeScript中的 interface 是唯一允许合理扩展的结构,因为它只是编译时/语法检查而不是运行时操作 .

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

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

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

    然后:

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

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

    在CodePlex上已有类似"mix-ins"之类的建议,但它们尚未实现 . 即使是混合使用的建议也不会完全直接使用,并且不会完全用TypeScript编写(因为使用JavaScript代码很容易,例如混合输入就无法安全地构建) .

  • 3

    您无法使用 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;
        }
    }
    
  • 12

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

    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", {});
    

    但它仍然有点奇怪

  • 22

    这可能吗?

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

    然后将CircleMarker实例定义为MyCircleMarker

相关问题