Pourquoi TypeScript autorise-t-il la duplication de composant pendant implements?

import { Component,OnInit } from '@angular/core';

export class CreateVersionComponent implements OnInit, OnInit, OnInit { }// no error
export class CreateVersionComponent extends OnInit, OnInit, OnInit { }// getting error

Mais cela génère une erreur d'identifiant en double lors de l'extension du composant .

Alors, quelle est la raison pour laquelle dactylographié accepte le composant dupliqué lors de l'implémentation? dans quelle situation devons-nous l'utiliser?

5
Ramesh Rajendran 15 nov. 2017 à 15:46

4 réponses

Meilleure réponse

Pour comprendre pourquoi le premier code n'est pas un problème, mais le second, vous devez comprendre la différence entre une classe et une interface. Une interface est une garantie que son implémenteur fournira au moins les membres de l'interface. Il ne fournit aucune fonctionnalité réelle. Une classe peut cependant contenir du code d'implémentation; vous pouvez hériter d'une classe pour réutiliser ce code et modifier son comportement en ajoutant un nouveau code ou en modifiant les implémentations existantes.

Cela signifie que implements et extends ont des significations différentes. implements dit: Je peux garantir à chaque consommateur de cette classe, qu'elle aura au moins les membres de l'interface. Puisqu'il n'y a pas d'implémentation réelle dans l'interface, il n'y a pas de problème à faire plusieurs de ces garanties, tant que la classe les implémente toutes. Vous avez raison de dire que cela n'a aucun sens d'ajouter la même garantie plusieurs fois, mais cela ne fait pas vraiment mal non plus. Les créateurs de TypeScript auraient pu interdire d'implémenter l'interface plusieurs fois. Nous pouvons spéculer pourquoi ils ne l'ont pas fait; je suppose que puisque TypeScript est basé sur JavaScript et que JS est assez indulgent, ils ne voulaient pas interdire quelque chose qui ne cause aucun dommage. Notez que TS est une couche de type au-dessus de JS et toutes les informations de type seront éventuellement supprimées lors de la compilation vers JS. Dans ce contexte, abandonner plusieurs implémentations d'interfaces répétitives ne fait pas vraiment de mal, car le résultat sera exactement le même.

À part implements, extends est une autre histoire. Bien que certains langages autorisent l'héritage multiple (par exemple C ++), l'héritage multiple s'accompagne de nombreux détails d'implémentation difficiles (tels que diamond problem ou appelant des constructeurs de classes de base), donc de nombreux langages ne le supportent pas avec l'idée qu'il cause plus de problèmes qu'il n'en résout. TypeScript n'autorise pas l'héritage multiple, ce qui signifie que vous ne pouvez pas utiliser extends avec plus d'une classe de base sur un principe général. Contrairement à l'implémentation d'une interface, l'héritage d'une classe a des conséquences sur le fonctionnement du programme et le compilateur fera bien plus que simplement supprimer les informations de type. C'est pourquoi il est logique d'y signaler une erreur.

7
Sefe 27 nov. 2017 à 08:40

TypeScript n'autorise pas l'héritage multiple, tout comme Java, C # etc. Donc le deuxième exemple ne fonctionnera pas d'abord parce que TSC pense que vous essayez d'étendre plus d'une classe, avant qu'il échoue parce que c'est la même classe.

Pour le premier cas, je suis d'accord qu'il faut dire quelque chose car c'est probablement une petite erreur. D'un autre côté, ce n'est pas sémantiquement faux. Lorsque vous implémentez les méthodes du premier OnInit, vous l'implémentez également pour le deuxième et le troisième, vous devriez donc être couvert.

1
Horia Coman 15 nov. 2017 à 13:09

La réponse est très simple.

Typescript n'autorise pas l ' héritage multiple .

Cela signifie qu'une classe ne peut pas hériter de plusieurs classes. (Ceci est pour «étend»).

Venons-en maintenant aux "implémentations" .

Dans ce cas, nous parlons des Interfaces . Vous pouvez implémenter autant d'interfaces dont vous avez besoin.

Gardez à l'esprit que l'implémentation d'interfaces ne signifie pas héritage. Cela signifie simplement que vous implémentez différents modèles sur votre classe, auxquels votre définition de classe doit adhérer.

Par conséquent, erreur sur les extensions (en raison de l'héritage multiple, ce qui n'est pas autorisé) Aucune erreur sur les outils (en raison de l'implémentation d'interfaces multiples, ce qui est autorisé)

1
Vinod Bhavnani 24 nov. 2017 à 10:05

Les réponses existantes expliquent bien:

  • Classe vs interface.
  • héritage multiple vs implémentation de plusieurs interfaces.
  • Les interfaces effacées par compilation signifient que le code compilé n'est pas différent des interfaces redondantes dans la clause implements.

Pourtant, une certaine confusion demeure. Pour aborder la question sous un autre angle:

dans quelle situation devons-nous l'utiliser?

Vous ne le faites pas. Il n'y a aucune situation dans laquelle vous devez déclarer des interfaces redondantes. Cela me rappelle quelque chose comme ça:

var v = v = v = 66

Oui , c'est très bien pour le compilateur. Non , vous n’avez jamais besoin de faire cela.

Pourquoi est-ce accepté?

Il est facile de comprendre pourquoi une personne (en particulier une personne ayant une expérience Java) pourrait être déroutée par l'absence d'avertissement. Après tout, Eclipse me met en garde à ce sujet depuis des années (Bonjour, Serializable!).

Avoir la même interface nommée plusieurs fois dans une seule définition de classe est un peu bizarre. Il peut être utile de considérer un exemple d'interface redondante qui est plus susceptible de se produire:

interface StringProducer {
    getString: () => string;
}

class Parent implements StringProducer {
    getString =  function(): string {
        return 'x';
    }
}

class Child extends Parent implements StringProducer {
    getString = function() : string {
        return 'y';
    }
}

class GrandChild extends Child implements StringProducer {
    getString = function(): string {
        return 'z';
    }
}

console.log(new Parent().getString());
console.log(new Child().getString());
console.log(new GrandChild().getString());

Vous pouvez (vaguement) penser à la classe GrandChild comme ceci:

public class GrandChild implements StringProducer, StringProducer, StringProducer {

Puisqu'une classe implémente toutes ses interfaces et celles de ses ancêtres.

Le compilateur (ou linter, peut-être) devrait-il aboyer à ce sujet? Dois-je être obligé de supprimer la clause implements de Child et GrandChild?

Je pense que c'est en grande partie une question de préférence. Par exemple, quand j'ai GrandChild ouvert dans mon IDE, je pourrais vouloir voir dans ce fichier toutes les interfaces implémentées par la classe. D'un autre côté, je pourrais avoir l'impression que ce n'est que du bruit et je veux un avertissement.

Le compilateur ne s'en soucie certainement pas et n'en a pas besoin. Mais je peux voir pourquoi vous voudrez peut-être un avertissement de peluche pour cela. La question à la fin de la journée me semble être "Pourquoi n'y a-t-il pas de règle tslint pour les interfaces redondantes?". C'est une question raisonnable, à laquelle je ne peux pas répondre. Vous pouvez toujours écrire une telle règle et la partager avec nous .

3
Mike Patrick 20 nov. 2017 à 19:44
47307808