Подтвердить что ты не робот

Как расширить определение класса TypeScript в отдельном файле определения?

У меня есть библиотека 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;
    }
}

Это имеет смысл, поскольку это класс, а не интерфейс, но в этом случае есть способ расширить это определение класса без изменения исходного файла определения?

Базовый файл определения втягивается от DefinitelyTyped через nuget, поэтому у меня очень сильное желание не вносить никаких изменений в него, так как это сделает обновление более неудобным/подверженным сбою.

4b9b3361

Ответ 1

Если вы не контролируете исходный файл определения и не можете вносить в него корректировки, то, к сожалению, то, что вы пытаетесь сделать, в настоящее время не поддерживается в TypeScript. interface в TypeScript - единственная конструкция, которая допускает разумные расширения, поскольку это только проверка времени компиляции/синтаксиса, а не операция времени выполнения.

Вы не можете расширять class в TypeScript новыми функциями, используя только TypeScript (и ожидая, что завершение кода /Intellisense будет работать как ожидалось). Конечно, вы могли бы добавить функции в 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, который просто нельзя было бы безопасно построить например, с перемешиванием).

Ответ 2

Вы не можете сделать это с помощью ключевого слова 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;
    }
}

Ответ 3

Это то, что я пробовал и чувствует себя относительно комфортно для меня.

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

где CircleMarkerEx и ex можно определить как

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

но все еще немного странно

Ответ 4

Возможно ли это?

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

И затем определите экземпляры CircleMarker как MyCircleMarker