Was ist der Unterschied zwischen "Klasse deklarieren" und "Schnittstelle" in TypeScript?

116

Was ist in TypeScript beim Erstellen von .d.ts-Quelldeklarationsdateien vorzuziehen und warum?

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

oder

interface Example {
    Method(): void;
}

Die Unterschiede, die ich feststellen kann, sind, dass Schnittstellen keine statischen Methoden haben können, daher müssen Sie dafür eine Klasse verwenden. Beide produzieren keine JS-Ausgabe, also spielt es vielleicht keine Rolle?

Chris
quelle
Ich glaube nicht, dass eine dieser beiden Aussagen wirklich dazu beiträgt, zu beschreiben, dass Sie eine über der anderen verwenden würden, da beide ohne JS-Ausgabe dasselbe erreichen können.
Chris
1
Kurz: Mit declare müssen Sie sicherstellen, dass die Implementierung der Klasse zur Laufzeit vorhanden ist, wo Sie dies mit einer Schnittstelle nicht tun müssen.
Hakim

Antworten:

163

interfaceist für den Fall, dass Sie einfach die Form eines Objekts beschreiben möchten. Es gibt niemals eine Codegenerierung für Schnittstellen - sie sind nur ein Artefakt im Typsystem. Sie werden keinen Unterschied in der Codegenerierung für eine Klasse sehen, abhängig davon, ob sie eine implementsKlausel enthält oder nicht .

declare classDies ist der Fall, wenn Sie eine vorhandene Klasse beschreiben möchten (normalerweise eine TypeScript-Klasse, aber nicht immer), die extern vorhanden sein wird (z. B. haben Sie zwei .ts-Dateien, die zu zwei .js-Dateien kompiliert werden, und beide werden über scriptTags eingeschlossen auf einer Webseite). Wenn Sie von einer classVerwendung erben extends(unabhängig davon, ob der Basistyp declare classein regulärer oder ein regulärer Typ war class), generiert der Compiler den gesamten Code, um die Prototypkette und die Weiterleitungskonstruktoren anzuschließen, und was nicht.

Wenn Sie versuchen, von declare classeiner Schnittstelle zu erben , die eine Schnittstelle sein sollte, tritt ein Laufzeitfehler auf, da dieser generierte Code auf ein Objekt ohne Laufzeitmanifestation verweist.

Umgekehrt, wenn Sie einfach implementeine Schnittstelle sind, die eine sein sollte declare class, müssen Sie alle Mitglieder selbst neu implementieren und werden keine Code-Wiederverwendung aus der potenziellen Basisklasse und den Funktionen nutzen Wenn Sie die Prototypenkette zur Laufzeit überprüfen, wird Ihr Objekt als keine Instanz der Basisklasse zurückgewiesen.

Um wirklich nerdig zu werden, wenn Sie einen C ++ - Hintergrund haben, können Sie sich grob interfaceals typedefund declare classals externDeklaration eines Konstruktors vorstellen, dem in dieser Kompilierungseinheit strikt eine Definition fehlt.

Von der reinen Verbraucherseite (Schreiben von imperativem Code, keine neuen Typen hinzufügen) besteht der einzige Unterschied zwischen interfaceund declare classdarin, dass Sie keine newSchnittstelle verwenden können. Wenn Sie jedoch beabsichtigen , extend/ implementeinen dieser Typen in einem neuen class, Sie haben absolut gewählt haben richtig zwischen interfaceund declare class. Nur einer von ihnen wird funktionieren.

Zwei Regeln, die Ihnen gut dienen:

  • Stimmt der Name des Typs mit einer Konstruktorfunktion überein (etwas, mit dem aufgerufen werden kann new), das zur Laufzeit tatsächlich vorhanden ist (z. B. Dateist, aber JQueryStaticnicht)? Wenn nein , möchten Sie auf jeden Fallinterface
  • Handle es sich um eine kompilierte Klasse aus einer anderen TypeScript-Datei oder um etwas ähnlich Ähnliches? Wenn ja , verwenden Siedeclare class
Ryan Cavanaugh
quelle
Sie können eine Schnittstelle in Typoskript tatsächlich neu erstellen. Die einzige Einschränkung ist die Vererbung.
Oleg Mihailik
3
Sie können den newOperator für einen Schnittstellentyp nicht aufrufen . Schnittstellen können jedoch Konstruktsignaturen haben, dh Sie können den newOperator für einen Wert des Schnittstellentyps aufrufen . Dies unterscheidet sich stark von der Funktionsweise class, bei der sich die Konstruktsignatur auf dem Typnamen selbst und nicht auf einem Ausdruck dieses Typs befindet.
Ryan Cavanaugh
Wenn Sie einer Schnittstelle einen Konstruktor hinzufügen, sollte dieser das EINZIGE Mitglied der Schnittstelle sein, mit Ausnahme der 'Statik' der Klasse. Kombinieren Sie die Schnittstelle der Konstruktorfunktion nicht mit der Schnittstelle des erstellten Objekts. Wenn Sie dies tun, erlaubt das Typsystem Dummheit wie: new (new x ()), wobei x: Interface.
Jeremy Bell
24

Sie können die Schnittstelle implementieren:

class MyClass implements Example {
    Method() {

    }
}

Während die declare classSyntax eigentlich dazu gedacht ist, Typdefinitionen für externen Code hinzuzufügen, der nicht in TypeScript geschrieben ist, erfolgt die Implementierung "anderswo".

Fenton
quelle
Sie schlagen also vor, die Deklarationsklasse zu verwenden, um Code zu beschreiben, der nicht in TypeScript geschrieben ist? Ich würde annehmen, dass der Fall, aber in der eingecheckten Datei jquery.d.ts ist JQueryStatic eine Schnittstelle, die implementiert wird von: declare var $: JQueryStatic Ich hätte allerdings gedacht, dass dies die Klasse $ {public static ...}
Chris
Der einzige Grund, den ich mir vorstellen kann, wäre, wenn Sie nicht möchten, dass die Klasse erweitert wird. Wenn Sie die Schnittstelle verwenden, müssen Sie die gesamte Implementierung bereitstellen.
Fenton
Macht Sinn. Vielleicht war das der Grund.
Chris
OK, dann versuche ich, auf Deklarationen in einer anderen JS-Bibliothek zu verweisen, daher muss ich definitiv deklarieren. Der Code verwendet Statik für einige der Bibliotheksfunktionen (Klassen). Bisher ist noch keine statische Eigenschaft oder Methode in einer Deklarationsdatei angegeben. Oh, und sollte ich einen Namespace oder ein Modul verwenden?
Jenson-Button-Event
13

In Laienbegriffen declarewird in .ts/ d.tsfiles verwendet, um dem Compiler mitzuteilen, dass das zu erwartende Schlüsselwort declaringin dieser Umgebung zu erwarten ist , auch wenn es in der vorliegenden Datei nicht definiert ist. Dies ermöglicht uns dann die Typensicherheit bei Verwendung des deklarierten Objekts, da der Typescript-Compiler jetzt weiß, dass eine andere Komponente diese Variable bereitstellen kann.

Nikk Wong
quelle
6

Unterschied zwischen declareund interfacein TS:

erklären:

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

Im obigen Code teilt declareder TS-Compiler mit, dass die Klasse irgendwo Exampledeklariert ist. Dies bedeutet nicht, dass die Klasse magisch eingeschlossen ist. Sie als Programmierer sind dafür verantwortlich, dass die Klasse verfügbar ist, wenn Sie sie deklarieren (mit dem declareSchlüsselwort).

Schnittstelle:

interface Example {
    Method(): void;
}

An interfaceist ein virtuelles Konstrukt, das nur innerhalb von Typoskript existiert. Der Typoskript-Compiler verwendet es ausschließlich zur Typprüfung. Wenn der Code in Javascript kompiliert wird, wird das gesamte Konstrukt entfernt. Der Typoskript-Compiler verwendet Schnittstellen, um zu überprüfen, ob Objekte die richtige Struktur haben.

Zum Beispiel, wenn wir die folgende Schnittstelle haben:

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

Die Objekte, die wir definieren und die diesen Schnittstellentyp haben, müssen genau mit der Schnittstelle übereinstimmen:

// perfect match has all the properties with the right types, TS compiler will not complain.
  const obj1: test = {   
    foo: 5,
    bar: 'hey',
  }
Willem van der Veen
quelle