J'essaie de comprendre comment définir correctement les méthodes abstraites en TypeScript:
En utilisant l'exemple d'héritage d'origine:
class Animal {
constructor(public name) { }
makeSound(input : string) : string;
move(meters) {
alert(this.name + " moved " + meters + "m.");
}
}
class Snake extends Animal {
constructor(name) { super(name); }
makeSound(input : string) : string {
return "sssss"+input;
}
move() {
alert("Slithering...");
super.move(5);
}
}
Je voudrais savoir comment définir correctement la méthode makeSound, donc elle est typée et possible de trop.
De plus, je ne sais pas comment définir correctement les protected
méthodes - cela semble être un mot-clé, mais n'a aucun effet et le code ne se compilera pas.
typescript
Vojtěch
la source
la source
Réponses:
La
name
propriété est marquée commeprotected
. Cela a été ajouté dans TypeScript 1.3 et est maintenant fermement établi.La
makeSound
méthode est marquée commeabstract
, tout comme la classe. Vous ne pouvez pas instancier directement unAnimal
maintenant, car il est abstrait. Cela fait partie de TypeScript 1.6 , qui est maintenant officiellement en ligne.L'ancienne façon d'imiter une méthode abstraite était de lancer une erreur si quelqu'un l'utilisait. Vous ne devriez plus avoir à faire cela une fois que TypeScript 1.6 atterrit dans votre projet:
la source
makeSound(input : number) : string {
basée sur l'exemple ci-dessus, oùinput
devrait être une chaîne.Type 'string' is not assignable to type 'number'.
.Si vous prenez la réponse d'Erics un peu plus loin, vous pouvez réellement créer une implémentation assez décente de classes abstraites, avec une prise en charge complète du polymorphisme et la possibilité d'appeler des méthodes implémentées à partir de la classe de base. Commençons par le code:
Étant donné que la
Animal
classe nécessite une implémentation de,IAnimal
il est impossible de construire un objet de typeAnimal
sans avoir une implémentation valide des méthodes abstraites. Notez que pour que le polymorphisme fonctionne, vous devez contourner les instances deIAnimal
, nonAnimal
. Par exemple:La principale différence ici avec la réponse d'Erics est que la classe de base "abstraite" nécessite une implémentation de l'interface, et ne peut donc pas être instanciée seule.
la source
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/
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:
et
la source
J'utilise pour lancer une exception dans la classe de base.
Ensuite, vous devez implémenter dans la sous-classe. Le contre est qu'il n'y a pas d'erreur de build mais d'exécution. Les avantages sont que vous pouvez appeler cette méthode à partir de la super classe, en supposant qu'elle fonctionnera :)
HTH!
Milton
la source
Non non Non!N'essayez pas de créer vos propres classes et méthodes «abstraites» lorsque le langage ne prend pas en charge cette fonctionnalité; il en va de même pour toute fonctionnalité de langue que vous souhaitez qu'une langue donnée soit prise en charge. Il n'existe aucun moyen correct d'implémenter des méthodes abstraites dans TypeScript. Structurez simplement votre code avec des conventions de dénomination telles que certaines classes ne soient jamais directement instanciées, mais sans appliquer explicitement cette interdiction.
En outre, l'exemple ci-dessus ne fournira cette application qu'au moment de l'exécution, PAS au moment de la compilation, comme vous pouvez vous y attendre en Java / C #.
la source