Qual è la differenza tra "declare class" e "interface" in TypeScript


116

In TypeScript, quando si creano file di dichiarazione del sorgente .d.ts, quale è preferibile e perché?

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

o

interface Example {
    Method(): void;
}

Le differenze che posso dire sono che le interfacce non possono avere metodi statici, quindi devi usare una classe per questo. Entrambi non producono alcun output JS, quindi forse non importa?



Non credo che nessuno di questi aiuti davvero a descrivere che useresti uno sull'altro poiché entrambi possono realizzare la stessa cosa senza output JS.
Chris

1
Breve: con declare devi assicurarti che l'implementazione della classe esista a runtime dove con un'interfaccia non devi.
Hakim

Risposte:


163

interfaceserve per descrivere semplicemente la forma di un oggetto. Non c'è mai nessuna generazione di codice per le interfacce: sono solo un artefatto nel sistema di tipi. Non vedrai alcuna differenza nella generazione del codice per una classe a seconda che abbia o meno una implementsclausola.

declare classserve quando vuoi descrivere una classe esistente (di solito una classe TypeScript, ma non sempre) che sarà presente esternamente (ad esempio, hai due file .ts che si compilano in due file .js ed entrambi sono inclusi tramite scripttag in una pagina web). Se erediti da un classusing extends(indipendentemente dal fatto che il tipo di base fosse a declare classo normale class) il compilatore genererà tutto il codice per collegare la catena di prototipi e i costruttori di inoltro e cosa no.

Se si tenta di ereditare da un'interfaccia declare classche avrebbe dovuto essere, si verificherà un errore di runtime perché il codice generato farà riferimento a un oggetto senza manifestazione di runtime.

Al contrario, se hai semplicemente implementun'interfaccia che avrebbe dovuto essere a declare class, dovrai reimplementare tutti i membri da solo e non trarrai vantaggio dal riutilizzo del codice dalla potenziale classe base e dalle funzioni che controlla la catena del prototipo in fase di esecuzione rifiuterà il tuo oggetto in quanto non è effettivamente un'istanza della classe base.

Per diventare davvero nerd, se hai uno sfondo C ++, puoi pensare interfacepiù typedefo meno declare classcome e come una externdichiarazione di un costruttore a cui manca rigorosamente una definizione in questa unità di compilazione.

Dal punto di vista del consumo puro (scrivere codice imperativo, non aggiungere nuovi tipi), l'unica differenza tra interfacee declare classè che non puoi newun'interfaccia. Tuttavia, se intendi extend/ implementuno di questi tipi in un nuovo class, devi assolutamente aver scelto correttamente tra interfacee declare class. Solo uno di loro funzionerà.

Due regole che ti serviranno bene:

  • Il nome del tipo si allinea con una funzione di costruzione (qualcosa invocabile con new) che è effettivamente presente in fase di esecuzione (ad esempio Dateè, ma JQueryStaticnon lo è)? Se no , lo vuoi sicuramenteinterface
  • Ho a che fare con una classe compilata da un altro file TypeScript o qualcosa di sufficientemente simile? Se , usadeclare class

In realtà puoi nuova un'interfaccia in dattiloscritto. L'unico limite è l'ereditarietà.
Oleg Mihailik

3
Non è possibile richiamare l' newoperatore su un tipo di interfaccia. Tuttavia, le interfacce possono avere firme di costruzione, il che significa che è possibile richiamare l' newoperatore su un valore del tipo di interfaccia. Questo è molto diverso da come classfunziona, dove la firma del costrutto si trova sul nome del tipo stesso piuttosto che su un'espressione di quel tipo.
Ryan Cavanaugh

Se segui la strada per aggiungere un costruttore a un'interfaccia, dovrebbe essere l'UNICO membro dell'interfaccia, ad eccezione di "statics" della classe. Non combinare l'interfaccia della funzione di costruzione con l'interfaccia dell'oggetto costruito. Se lo fai, il sistema di tipi consente stupidaggini come: new (new x ()), dove x: Interface.
Jeremy Bell

24

Puoi implementare l'interfaccia:

class MyClass implements Example {
    Method() {

    }
}

Considerando che la declare classsintassi è realmente intesa per essere utilizzata per aggiungere definizioni di tipo per codice esterno che non è scritto in TypeScript, quindi l'implementazione è "altrove".


Quindi stai suggerendo che la classe declare dovrebbe essere usata per descrivere il codice non scritto in TypeScript? Presumo che sia così, ma nel file jquery.d.ts archiviato, JQueryStatic è un'interfaccia implementata da: declare var $: JQueryStatic Avrei pensato che fosse dichiarare class $ {public static ...}
Chris

L'unico motivo a cui posso pensare per questo sarebbe se non volessi che le persone estendessero la classe - usare l'interfaccia significa che dovresti fornire l'intera implementazione.
Fenton

Ha senso. Forse quella era la ragione.
Chris

OK, quindi sto cercando di indicare le dichiarazioni in un'altra libreria JS, quindi ho decisamente bisogno di dichiarare. Il codice utilizza la statica su alcune delle funzioni di libreria (classi) - finora devo ancora vedere una proprietà o un metodo statico espresso in un file di dichiarazione. Oh, e dovrei usare lo spazio dei nomi o il modulo?
evento jenson-button

13

In parole povere, declareviene utilizzato in .ts/ d.tsfiles per dire al compilatore che dovremmo aspettarci che la parola chiave siamo declaringpresenti in quell'ambiente, anche se non è definita nel file presente. Questo ci consentirà quindi di avere l'indipendenza dai tipi quando si utilizza l'oggetto dichiarato, poiché il compilatore Typescript ora sa che qualche altro componente può fornire quella variabile.


6

Differenza tra declaree interfacein TS:

dichiarare:

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

Nel codice sopra si declarefa sapere al compilatore TS che da qualche parte Exampleè dichiarata la classe . Ciò non significa che la classe sia magicamente inclusa. Tu come programmatore sei responsabile di avere la classe disponibile quando la dichiari (con la declareparola chiave).

interfaccia:

interface Example {
    Method(): void;
}

An interfaceè un costrutto virtuale che esiste solo all'interno di un dattiloscritto. Il compilatore dattiloscritto lo utilizza per il solo scopo di controllo del tipo. Quando il codice viene compilato in javascript, l'intero costrutto verrà rimosso. Il compilatore di dattiloscritti utilizza interfacce per verificare se gli oggetti hanno la struttura corretta.

Ad esempio quando abbiamo la seguente interfaccia:

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

Gli oggetti che definiamo che hanno questo tipo di interfaccia devono corrispondere esattamente all'interfaccia:

// perfect match has all the properties with the right types, TS compiler will not complain.
  const obj1: test = {   
    foo: 5,
    bar: 'hey',
  }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.