Je crois que l'utilisation d'une combinaison d'interfaces et de classes de base pourrait fonctionner pour vous. Il appliquera les exigences comportementales au moment de la compilation (rq_ post "ci-dessous" fait référence à un post ci-dessus, qui n'est pas celui-ci).
L'interface définit l'API comportementale qui n'est pas satisfaite par la classe de base. Vous ne pourrez pas définir des méthodes de classe de base pour appeler des méthodes définies dans l'interface (car vous ne pourrez pas implémenter cette interface dans la classe de base sans avoir à définir ces comportements). Peut-être que quelqu'un peut trouver un coffre - fort astuce pour autoriser l'appel des méthodes d'interface dans le parent.
Vous devez vous rappeler d'étendre et d'implémenter dans la classe que vous instancierez. Il répond aux préoccupations concernant la définition du code d'échec d'exécution. Vous ne pourrez également même pas appeler les méthodes qui gêneraient si vous n'avez pas implémenté l'interface (comme si vous essayez d'instancier la classe Animal). J'ai essayé d'avoir l'interface étendre le BaseAnimal ci-dessous, mais il a caché le constructeur et le champ 'nom' de BaseAnimal de Snake. Si j'avais pu le faire, l'utilisation d'un module et des exportations aurait pu empêcher une instanciation directe accidentelle de la classe BaseAnimal.
Collez-le ici pour voir si cela fonctionne pour vous: 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);
Je suis tombé sur la réponse de FristvanCampen comme indiqué ci-dessous. Il dit que les classes abstraites sont un anti-modèle, et suggère que l'on instancie les classes «abstraites» de base en utilisant une instance injectée d'une classe d'implémentation. C'est juste, mais des arguments contraires sont avancés. Lisez par vous-même:
https://typescript.codeplex.com/discussions/449920
Partie 2: J'ai eu un autre cas où je voulais une classe abstraite, mais on m'a empêché d'utiliser ma solution ci-dessus, car les méthodes définies dans la "classe abstraite" devaient se référer aux méthodes définies dans l'interface correspondante. Donc, j'utilise les conseils de FristvanCampen, en quelque sorte. J'ai la classe "abstraite" incomplète, avec les implémentations de méthodes. J'ai l'interface avec les méthodes non implémentées; cette interface étend la classe "abstraite". J'ai ensuite une classe qui étend la première et implémente la seconde (elle doit s'étendre à la fois car le super constructeur est inaccessible autrement). Voir l'exemple (non exécutable) ci-dessous:
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;
}
}
et
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;
}