Quelle est la différence entre «declare class» et «interface» dans TypeScript


116

Dans TypeScript, lors de la création de fichiers de déclaration source .d.ts, ce qui est préférable et pourquoi?

declare class Example {
    public Method(): void; 
}

ou

interface Example {
    Method(): void;
}

Les différences que je peux dire sont que les interfaces ne peuvent pas avoir de méthodes statiques, vous devez donc utiliser une classe pour cela. Les deux ne produisent aucune sortie JS, alors peut-être que cela n'a pas d'importance?



Je ne pense pas que l'un ou l'autre de ces éléments aide vraiment à décrire que vous utiliseriez l'un sur l'autre car ils peuvent tous les deux accomplir la même chose sans sortie JS.
Chris

1
Bref: Avec declare, vous devez vous assurer que l'implémentation de la classe existe au moment de l'exécution alors qu'avec une interface vous n'avez pas à le faire.
Hakim le

Réponses:


163

interfaceest pour quand vous voulez simplement décrire la forme d'un objet. Il n'y a jamais de génération de code pour les interfaces - elles sont uniquement un artefact dans le système de types. Vous ne verrez aucune différence dans la génération de code pour une classe selon qu'elle contient ou non une implementsclause.

declare classest lorsque vous souhaitez décrire une classe existante (généralement une classe TypeScript, mais pas toujours) qui va être présente en externe (par exemple, vous avez deux fichiers .ts qui se compilent en deux fichiers .js et les deux sont inclus via des scriptbalises dans une page Web). Si vous héritez d'une classutilisation extends(indépendamment du fait que le type de base soit a declare classou normal class), le compilateur va générer tout le code pour connecter la chaîne de prototypes et les constructeurs de transfert et autres.

Si vous essayez d'hériter d' declare classune interface qui aurait dû être une interface, vous allez avoir une erreur d'exécution car ce code généré fera référence à un objet sans manifestation d'exécution.

Inversement, si vous êtes simplement implementune interface qui aurait dû être a declare class, vous allez devoir réimplémenter tous les membres vous-même et ne profiterez pas de la réutilisation du code de la classe de base potentielle et des fonctions qui vérifie la chaîne de prototypes lors de l'exécution rejettera votre objet comme n'étant pas réellement une instance de la classe de base.

Pour devenir vraiment ringard, si vous avez un arrière-plan C ++, vous pouvez à peu près considérer interfacecomme typedefet declare classcomme une externdéclaration d'un constructeur qui manque strictement de définition dans cette unité de compilation.

Du point de vue de la consommation pure (écriture de code impératif, pas d'ajout de nouveaux types), la seule différence entre interfaceet declare classest que vous ne pouvez pas newune interface. Cependant, si vous avez l'intention de extend/ l' implementun de ces types dans un nouveau class, vous devez absolument avoir choisi correctement entre interfaceet declare class. Un seul d'entre eux fonctionnera.

Deux règles qui vous serviront bien:

  • Le nom du type s'aligne-t-il sur une fonction de constructeur (quelque chose d'invocable avec new) qui est réellement présente à l'exécution (par exemple Date, mais JQueryStaticn'est pas)? Si non , tu veux vraimentinterface
  • Ai-je affaire à une classe compilée à partir d'un autre fichier TypeScript ou à quelque chose de suffisamment similaire? Si oui , utilisezdeclare class

En fait, vous pouvez créer une nouvelle interface en tapuscrit. La seule limitation est l'héritage.
Oleg Mihailik

3
Vous ne pouvez pas appeler l' newopérateur sur un type d'interface. Cependant, les interfaces peuvent avoir des signatures de construction, ce qui signifie que vous pouvez appeler l' newopérateur sur une valeur du type d'interface. Ceci est très différent de la façon dont classfonctionne, où la signature de construction est sur le nom du type lui-même plutôt que sur une expression de ce type.
Ryan Cavanaugh

Si vous suivez la voie de l'ajout d'un constructeur à une interface, ce devrait être le SEUL membre de l'interface, à l'exception de la «statique» de la classe. Ne combinez pas l'interface de la fonction constructeur avec l'interface de l'objet construit. Si vous le faites, le système de types autorise des bêtises comme: new (new x ()), où x: Interface.
Jeremy Bell

24

Vous pouvez implémenter l'interface:

class MyClass implements Example {
    Method() {

    }
}

Alors que la declare classsyntaxe est vraiment destinée à être utilisée pour ajouter des définitions de type pour le code externe qui n'est pas écrit en TypeScript - donc l'implémentation est «ailleurs».


Vous suggérez donc que la classe declare soit utilisée pour décrire du code non écrit en TypeScript? Je suppose que c'est le cas, mais dans le fichier jquery.d.ts archivé, JQueryStatic est une interface implémentée par: declare var $: JQueryStatic J'aurais bien voulu que ce soit déclarer la classe $ {public static ...}
Chris

La seule raison à laquelle je peux penser pour cela serait que vous ne vouliez pas que les gens étendent la classe - en utilisant l'interface, vous devrez fournir toute l'implémentation.
Fenton

Logique. C'était peut-être la raison.
Chris

OK alors, j'essaie donc de pointer vers des déclarations dans une autre bibliothèque JS, donc j'ai absolument besoin de déclarer. Le code utilise la statique sur certaines des fonctions de la bibliothèque (classes) - jusqu'à présent, je n'ai pas encore vu une propriété ou une méthode statique exprimée dans un fichier de déclaration. Oh, et devrais-je utiliser un espace de noms ou un module?
jenson-button-event

13

En termes simples, declareest utilisé dans .ts/ d.tsfiles pour indiquer au compilateur que nous devrions nous attendre à ce que le mot clé que nous ayons declaringexiste dans cet environnement, même s'il n'est pas défini dans le fichier actuel. Cela nous permettra ensuite d'avoir une sécurité de type lors de l'utilisation de l'objet déclaré, car le compilateur Typescript sait maintenant qu'un autre composant peut fournir cette variable.


6

Différence entre declareet interfacedans TS:

déclarer:

declare class Example {
    public Method(): void; 
}

Dans le code ci-dessus, declarele compilateur TS sait que quelque part la classe Exampleest déclarée. Cela ne signifie pas que la classe est incluse comme par magie. En tant que programmeur, vous êtes responsable de la disponibilité de la classe lorsque vous la déclarez (avec le declaremot - clé).

interface:

interface Example {
    Method(): void;
}

An interfaceest une construction virtuelle qui n'existe que dans dactylographié. Le compilateur dactylographié l'utilise dans le seul but de vérifier le type. Lorsque le code est compilé en javascript, toute cette construction sera supprimée. Le compilateur dactylographié utilise des interfaces afin de vérifier si les objets ont la bonne structure.

Par exemple lorsque nous avons l'interface suivante:

interface test {
  foo: number,
  bar: string,
}

Les objets que nous définissons qui ont ce type d'interface doivent correspondre exactement à l'interface:

// perfect match has all the properties with the right types, TS compiler will not complain.
  const obj1: test = {   
    foo: 5,
    bar: 'hey',
  }
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.