Как расширить определение класса 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, поэтому у меня очень сильное желание не вносить никаких изменений в него, так как это сделает обновление более неудобным/подверженным сбою.
Ответы
Ответ 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