¿Cuál es la diferencia entre "declarar clase" e "interfaz" en TypeScript?

116

En TypeScript, al crear archivos de declaración de origen .d.ts, ¿cuál es preferible y por qué?

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

o

interface Example {
    Method(): void;
}

Las diferencias que puedo decir son que las interfaces no pueden tener métodos estáticos, por lo que debe usar una clase para eso. Ambos no producen ninguna salida JS, por lo que quizás no importe.

Chris
fuente
No creo que ninguno de estos realmente ayude a describir que usaría uno sobre otro, ya que ambos pueden lograr lo mismo sin salida JS.
Chris
1
Breve: Con declare, debe asegurarse de que la implementación de la clase exista en tiempo de ejecución donde con una interfaz no es necesario.
Hakim

Respuestas:

163

interfacees para cuando simplemente desea describir la forma de un objeto. Nunca hay generación de código para las interfaces; son únicamente un artefacto en el sistema de tipos. No verá ninguna diferencia en la generación de código para una clase dependiendo de si tiene o no una implementscláusula.

declare classes para cuando desea describir una clase existente (generalmente una clase TypeScript, pero no siempre) que estará presente externamente (por ejemplo, tiene dos archivos .ts que se compilan en dos archivos .js y ambos se incluyen a través de scriptetiquetas en una página web). Si hereda de un classusing extends(independientemente de si el tipo base era declare classao regular class), el compilador generará todo el código para conectar la cadena de prototipos y los constructores de reenvío y demás.

Si intenta heredar de una declare classque debería haber sido una interfaz, tendrá un error de tiempo de ejecución porque el código generado se referirá a un objeto sin manifestación de tiempo de ejecución.

Por el contrario, si simplemente tiene implementuna interfaz que debería haber sido una declare class, tendrá que volver a implementar todos los miembros usted mismo y no aprovechará la reutilización de código de la clase base y las funciones posibles. que verifique la cadena de prototipos en tiempo de ejecución rechazará su objeto por no ser realmente una instancia de la clase base.

Para conseguir realmente nerd, si usted tiene un fondo ++ C, se puede pensar en más o menos interfacecomo typedefy declare classcomo una externdeclaración de un constructor que tiene falta de una definición estricta en esta unidad de compilación.

Desde el punto de vista del consumo puro (escribir código imperativo, no agregar nuevos tipos), la única diferencia entre interfacey declare classes que no puede tener newuna interfaz. Sin embargo, si tiene la intención de extend/ implementuno de estos tipos en un nuevo class, es absolutamente necesario que haya elegido correctamente entre interfacey declare class. Solo uno de ellos funcionará.

Dos reglas que te servirán bien:

  • ¿El nombre del tipo se alinea con una función constructora (algo invocable con new) que está realmente presente en tiempo de ejecución (por ejemplo Date, está, pero JQueryStaticno está)? Si no , definitivamente quieresinterface
  • ¿Estoy tratando con una clase compilada de otro archivo TypeScript o algo suficientemente similar? Si , el usodeclare class
Ryan Cavanaugh
fuente
En realidad, puede crear una nueva interfaz en mecanografiado. La única limitación es la herencia.
Oleg Mihailik
3
No puede invocar al newoperador en un tipo de interfaz. Sin embargo, las interfaces pueden tener firmas de construcción, lo que significa que puede invocar al newoperador en un valor del tipo de interfaz. Esto es muy diferente de cómo classfunciona, donde la firma de construcción está en el nombre del tipo en sí en lugar de en una expresión de ese tipo.
Ryan Cavanaugh
Si sigue la ruta de agregar un constructor a una interfaz, debería ser el ÚNICO miembro de la interfaz, excepto por las 'estáticas' de la clase. No combine la interfaz de la función constructora con la interfaz del objeto construido. Si lo hace, el sistema de tipos permite tonterías como: new (new x ()), donde x: Interface.
Jeremy Bell
24

Puede implementar la interfaz:

class MyClass implements Example {
    Method() {

    }
}

Mientras que la declare classsintaxis está realmente pensada para ser utilizada para agregar definiciones de tipo para código externo que no está escrito en TypeScript, la implementación está "en otra parte".

Fenton
fuente
Entonces, ¿está sugiriendo que la clase declare debería usarse para describir código no escrito en TypeScript? Asumiría que es el caso, pero en el archivo jquery.d.ts registrado, JQueryStatic es una interfaz implementada por: declare var $: JQueryStatic Sin embargo, tendría que declarar la clase $ {public static ...}
Chris
La única razón en la que puedo pensar para esto sería si no quisieras que la gente extendiera la clase; usar la interfaz significa que tendrías que proporcionar toda la implementación.
Fenton
Tiene sentido. Quizás esa fue la razón.
Chris
Bien, entonces estoy tratando de apuntar a declaraciones en otra biblioteca JS, así que definitivamente necesito declarar. El código usa estática en algunas de las funciones de la biblioteca (clases); hasta ahora, todavía no he visto una propiedad o método estático expresado en un archivo de declaración. Ah, ¿y debería usar un espacio de nombres o un módulo?
jenson-button-event
13

En términos simples, declarese usa en .ts/ d.tsfiles para decirle al compilador que debemos esperar que la palabra clave we declaringexista en ese entorno, incluso si no está definida en el archivo actual. Esto nos permitirá tener seguridad de tipos cuando usemos el objeto declarado, ya que el compilador de TypeScript ahora sabe que algún otro componente puede proporcionar esa variable.

nikk wong
fuente
6

Diferencia entre declarey interfaceen TS:

declarar:

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

En el código anterior, declareel compilador de TS sabe que en algún lugar Examplese declara la clase . Esto no significa que la clase esté incluida mágicamente. Tú, como programador, eres responsable de tener la clase disponible cuando la declaras (con la declarepalabra clave).

interfaz:

interface Example {
    Method(): void;
}

An interfacees una construcción virtual que solo existe dentro de mecanografiado. El compilador de mecanografiado lo usa con el único propósito de verificar el tipo. Cuando el código se compila en JavaScript, toda esta construcción se eliminará. El compilador de mecanografiado utiliza interfaces para comprobar si los objetos tienen la estructura correcta.

Por ejemplo cuando tenemos la siguiente interfaz:

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

Los objetos que definimos que tienen este tipo de interfaz deben coincidir exactamente con la interfaz:

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