Я считаю, что использование комбинации интерфейсов и базовых классов может работать для вас. Он будет обеспечивать соблюдение поведенческих требований во время компиляции (rq_ post «ниже» относится к посту выше, а не к этому).
Интерфейс устанавливает поведенческий API, который не соответствует базовому классу. Вы не сможете настроить методы базового класса для вызова методов, определенных в интерфейсе (потому что вы не сможете реализовать этот интерфейс в базовом классе без необходимости определять эти поведения). Может быть, кто-то может придумать безопасный прием, позволяющий вызывать методы интерфейса в родительском.
Вы должны помнить, чтобы расширять и реализовывать в классе, который вы создадите. Это удовлетворяет опасениям по поводу определения кода ошибки времени выполнения. Вы также не сможете даже вызывать методы, которые вылились бы, если бы вы не реализовали интерфейс (например, если вы пытаетесь создать экземпляр класса Animal). Я попытался сделать так, чтобы интерфейс расширял BaseAnimal ниже, но он скрыл конструктор и поле 'name' BaseAnimal от Snake. Если бы мне удалось это сделать, использование модуля и экспортов могло бы предотвратить случайное прямое создание экземпляра класса BaseAnimal.
Вставьте это здесь, чтобы увидеть, работает ли оно для вас: http://www.typescriptlang.org/Playground/
// The behavioral interface also needs to extend base for substitutability
interface AbstractAnimal extends BaseAnimal {
// encapsulates animal behaviors that must be implemented
makeSound(input : string): string;
}
class BaseAnimal {
constructor(public name) { }
move(meters) {
alert(this.name + " moved " + meters + "m.");
}
}
// If concrete class doesn't extend both, it cannot use super methods.
class Snake extends BaseAnimal implements AbstractAnimal {
constructor(name) { super(name); }
makeSound(input : string): string {
var utterance = "sssss"+input;
alert(utterance);
return utterance;
}
move() {
alert("Slithering...");
super.move(5);
}
}
var longMover = new Snake("windy man");
longMover.makeSound("...am I nothing?");
longMover.move();
var fulture = new BaseAnimal("bob fossil");
// compile error on makeSound() because it is not defined.
// fulture.makeSound("you know, like a...")
fulture.move(1);
Я наткнулся на ответ FristvanCampen, как указано ниже. Он говорит, что абстрактные классы являются антишаблоном, и предлагает создать экземпляр базовых «абстрактных» классов, используя внедренный экземпляр реализующего класса. Это справедливо, но есть контраргументы. Прочитайте для себя:
https://typescript.codeplex.com/discussions/449920
Часть 2: У меня был другой случай, когда я хотел абстрактный класс, но я не мог использовать свое решение выше, потому что определенные методы в «абстрактном классе» должны были ссылаться на методы, определенные в соответствующем интерфейсе. Итак, я использую совет FristvanCampen. У меня есть неполный "абстрактный" класс, с реализациями методов. У меня есть интерфейс с нереализованными методами; этот интерфейс расширяет «абстрактный» класс. Затем у меня есть класс, который расширяет первый и реализует второй (он должен расширять оба, потому что в противном случае супер-конструктор недоступен). Смотрите (неработающий) пример ниже:
export class OntologyConceptFilter extends FilterWidget.FilterWidget<ConceptGraph.Node, ConceptGraph.Link> implements FilterWidget.IFilterWidget<ConceptGraph.Node, ConceptGraph.Link> {
subMenuTitle = "Ontologies Rendered"; // overload or overshadow?
constructor(
public conceptGraph: ConceptGraph.ConceptGraph,
graphView: PathToRoot.ConceptPathsToRoot,
implementation: FilterWidget.IFilterWidget<ConceptGraph.Node, ConceptGraph.Link>
){
super(graphView);
this.implementation = this;
}
}
и
export class FilterWidget<N extends GraphView.BaseNode, L extends GraphView.BaseLink<GraphView.BaseNode>> {
public implementation: IFilterWidget<N, L>
filterContainer: JQuery;
public subMenuTitle : string; // Given value in children
constructor(
public graphView: GraphView.GraphView<N, L>
){
}
doStuff(node: N){
this.implementation.generateStuff(thing);
}
}
export interface IFilterWidget<N extends GraphView.BaseNode, L extends GraphView.BaseLink<GraphView.BaseNode>> extends FilterWidget<N, L> {
generateStuff(node: N): string;
}