Qual é a diferença entre “declarar classe” e “interface” no TypeScript

116

No TypeScript, ao criar arquivos de declaração de origem .d.ts, o que é preferível e por quê?

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

ou

interface Example {
    Method(): void;
}

As diferenças que posso dizer são que as interfaces não podem ter métodos estáticos, então você deve usar uma classe para isso. Ambos não produzem nenhuma saída JS, então talvez isso não importe?

Chris
fonte
Consulte stackoverflow.com/a/14323673/1704166
Ryan Cavanaugh
Não acho que qualquer um desses realmente ajuda a descrever que você usaria um em vez do outro, pois ambos podem realizar a mesma coisa sem saída JS.
Chris
1
Resumindo: com declare, você deve ter certeza de que a implementação da classe existe em tempo de execução, onde com uma interface você não precisa.
Hakim

Respostas:

163

interfaceé para quando você simplesmente deseja descrever a forma de um objeto. Não há geração de código, nunca, para interfaces - elas são apenas um artefato no sistema de tipos. Você não verá nenhuma diferença na geração de código para uma classe, dependendo se ela tem ou não uma implementscláusula.

declare classé para quando você deseja descrever uma classe existente (geralmente uma classe TypeScript, mas nem sempre) que estará presente externamente (por exemplo, você tem dois arquivos .ts que compilam para dois arquivos .js e ambos são incluídos por meio de scripttags em uma página da web). Se você herdar de um classusing extends(independentemente de o tipo base ser a declare classou regular class), o compilador irá gerar todo o código para conectar a cadeia de protótipos e os construtores de encaminhamento e quais não.

Se você tentar herdar de um declare classque deveria ser uma interface, ocorrerá um erro de tempo de execução porque o código gerado estará se referindo a um objeto sem manifestação de tempo de execução.

Por outro lado, se você simplesmente implementuma interface que deveria ser um declare class, você terá que reimplementar todos os membros você mesmo e não tirará proveito de qualquer reutilização de código da suposta classe de base e funções que verificar a cadeia de protótipo em tempo de execução rejeitará seu objeto como não sendo realmente uma instância da classe base.

Para ficar realmente nerd, se você tem experiência em C ++, você pode pensar aproximadamente interfacecomo typedefe declare classcomo uma externdeclaração de um construtor que carece estritamente de uma definição nesta unidade de compilação.

Do ponto de vista do consumo puro (escrever código imperativo, não adicionar novos tipos), a única diferença entre interfacee declare classé que você não pode usar newuma interface. No entanto, se você pretende extend/ implementum desses tipos em um novo class, é absolutamente necessário ter escolhido corretamente entreinterface e declare class. Apenas um deles funcionará.

Duas regras que lhe servirão bem:

  • O nome do tipo está alinhado com uma função construtora (algo invocável new) que está realmente presente no tempo de execução (por exemplo Date, está, mas JQueryStaticnão está)? Se não , você definitivamente querinterface
  • Estou lidando com uma classe compilada de outro arquivo TypeScript ou algo suficientemente semelhante? Se sim , usedeclare class
Ryan Cavanaugh
fonte
Na verdade, você pode criar uma nova interface em texto digitado. A única limitação é a herança.
Oleg Mihailik
3
Você não pode chamar o newoperador em um tipo de interface. No entanto, as interfaces podem ter assinaturas de construção, o que significa que você pode chamar o newoperador em um valor do tipo de interface. Isso é muito diferente de como classfunciona, em que a assinatura da construção está no próprio nome do tipo, e não em uma expressão desse tipo.
Ryan Cavanaugh
Se você seguir o caminho de adicionar um construtor a uma interface, ele deve ser o ÚNICO membro da interface, exceto para 'estática' da classe. Não combine a interface da função do construtor com a interface do objeto construído. Se você fizer isso, o sistema de tipo permite bobagens como: novo (novo x ()), onde x: Interface.
Jeremy Bell
24

Você pode implementar a interface:

class MyClass implements Example {
    Method() {

    }
}

Considerando que a declare classsintaxe realmente se destina a ser usada para adicionar definições de tipo para código externo que não é escrito em TypeScript - portanto, a implementação é "em outro lugar".

Fenton
fonte
Então você está sugerindo que declare class deve ser usado para descrever código não escrito em TypeScript? Eu presumiria que fosse o caso, mas no arquivo jquery.d.ts verificado, JQueryStatic é uma interface implementada por: declare var $: JQueryStatic que eu teria pensado que isso fosse declarar class $ {public static ...}
Chris
A única razão que posso pensar para isso seria se você não quisesse que as pessoas estendessem a classe - usar a interface significa que você teria que fornecer toda a implementação.
Fenton de
Faz sentido. Talvez seja esse o motivo.
Chris
OK então, estou tentando apontar para declarações em outra biblioteca JS, então eu definitivamente exijo declaração. O código está usando estática em algumas das funções da biblioteca (classes) - até agora, ainda não vi uma propriedade ou método estático expresso em um arquivo de declaração. Ah, e devo usar namespace ou módulo?
jenson-button-event
13

Em termos gerais, declareé usado em .ts/ d.tsfiles para dizer ao compilador que devemos esperar que a palavra-chave declaringexistente exista naquele ambiente, mesmo que não esteja definida no arquivo presente. Isso nos permitirá ter segurança de tipo ao usar o objeto declarado, pois o compilador Typescript agora sabe que algum outro componente pode fornecer essa variável.

nikk wong
fonte
6

Diferença entre declareeinterface em TS:

declarar:

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

No código acima, declarepermite ao compilador TS saber que em algum lugar a classe Exampleestá declarada. Isso não significa que a classe está magicamente incluída. Você, como programador, é responsável por ter a classe disponível quando você a declara (com odeclare palavra chave).

interface:

interface Example {
    Method(): void;
}

A interface é uma construção virtual que existe apenas no texto digitado. O compilador de texto digitado o usa com o único propósito de verificação de tipo. Quando o código é compilado para javascript, toda a construção será eliminada. O compilador de texto digitado usa interfaces para verificar se os objetos têm a estrutura correta.

Por exemplo, quando temos a seguinte interface:

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

Os objetos que definimos que têm este tipo de interface precisam corresponder exatamente à interface:

// 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
fonte