javascript - two - Interfaz y clase en TypeScript
typescript struct (5)
¿Cómo deberían utilizarse las interfaces de manera adecuada en TypeScript: para establecer contratos de comportamiento, o para definir las propiedades y el objeto debería tener?
Las interfaces en TypeScript son contratos de formas que describen la estructura esperada de un objeto. Si un valor tiene una determinada anotación de interfaz, se espera que sea un objeto con los miembros definidos en la interfaz. Los miembros pueden ser valores o funciones (métodos). Generalmente, su comportamiento (cuerpos de función) no es parte del contrato. Pero puede especificar si son de readonly
o no.
Entonces, ¿para qué están diseñadas las interfaces en TypeScript? ¿Por qué las personas usan interfaces en TypeScript como usamos clases en C #?
Las interfaces de tipografía pueden jugar el mismo rol que las interfaces C # si se espera que sean implementadas por las clases de TypeScript.
Pero no solo una clase puede implementar una interfaz; cualquier tipo de valor puede:
interface HelloPrinter {
printHello(): void
}
El siguiente objeto no es una clase pero, sin embargo, implementa la interfaz:
{
printHello: () => console.log("hello")
}
Por lo tanto, podemos hacer
const o: HelloPrinter = {
printHello: () => console.log("hello")
}
y el compilador de TypeScript no se quejará.
El objeto implementa nuestra interfaz sin obligarnos a escribir una clase.
Trabajar con interfaces es más ligero que trabajar con (interfaces y) clases.
Pero si necesita saber el nombre del tipo (nombre de clase / interfaz) durante el tiempo de ejecución, las clases son la elección correcta, ya que los nombres de interfaz solo se conocen en tiempo de compilación.
En C # hay una gran diferencia entre interfaces y clases. De hecho, una clase representa un tipo de referencia, de modo que podemos crear objetos modelados en esa clase, mientras que las interfaces son contratos a los que una clase se apunta para garantizar la existencia de un determinado comportamiento. En particular, no podemos crear instancias de interfaces.
El punto entero con las interfaces es exponer el comportamiento. Una clase lo implementa dando una implementación explícita de dicho comportamiento.
En ese caso, aunque las interfaces pueden contener propiedades, la mayoría de las veces nos preocupamos por las interfaces debido a problemas de comportamiento. Entonces, la mayoría del tipo, las interfaces son solo contratos de comportamiento.
En TypeScript, por otro lado, parezco algo que me inquietó bastante, y en verdad lo he visto más de una vez, y esta es la razón de esta pregunta.
En un tutorial vi esto:
export interface User {
name: string; // required with minimum 5 chracters
address?: {
street?: string; // required
postcode?: string;
}
}
Pero espera un minuto ¿Por qué el User
es una interfaz? Si pensamos como C #, el User
no debe ser una interfaz. En verdad, mirándolo, parece que estamos definiendo el tipo de datos User
, en lugar de un contrato de comportamiento.
Pensando como lo hacemos en C #, lo natural sería esto:
export class User {
public name: string;
public address: Address;
}
export class Address {
public street: string;
public postcode: string;
}
Pero esto de usar interfaces como lo hacemos con las clases, simplemente definir un tipo de datos, en lugar de definir un contrato de comportamiento, parece muy común en TypeScript.
Entonces, ¿para qué están diseñadas las interfaces en TypeScript? ¿Por qué las personas usan interfaces en TypeScript como usamos clases en C #? ¿Cómo deberían utilizarse las interfaces de manera adecuada en TypeScript: para establecer contratos de comportamiento, o para definir las propiedades y el objeto debería tener?
Considere que en Javascript, los datos a menudo se intercambian como objetos simples, a menudo a través de JSON:
let data = JSON.parse(someString);
Digamos que esta data
es una matriz de objetos de User
y la pasaremos a una función:
data.forEach(user => foo(user))
foo
sería escrito de esta manera:
function foo(user: User) { ... }
Pero espera, en ningún momento hicimos un new User
! ¿Deberíamos? ¿Deberíamos escribir un User
clase y map
todos los data
, aunque el resultado sería exactamente el mismo, un Object
con propiedades? No, eso sería una locura solo por satisfacer el sistema de tipos, pero no cambiaría nada en el tiempo de ejecución. Aquí es perfectamente suficiente una interface
simple que describe cómo debe verse el objeto específico ("comportarse").
Las interfaces en texto mecanografiado son similares a las interfaces en C # en que ambos proporcionan un contrato. Sin embargo, opuesto a las interfaces C # que solo contienen métodos, las interfaces mecanografiadas también pueden describir campos o propiedades que contienen los objetos. Por lo tanto, también se pueden usar para cosas que no son directamente posibles con las interfaces C #.
Una gran diferencia entre las interfaces y las clases en mecanografiado es que las interfaces no tienen una representación en tiempo de ejecución y no habrá ningún código emitido para ellas. Las interfaces son ampliamente utilizables. Por ejemplo, puede usar literales de objetos para construir objetos con una interfaz. Me gusta:
let user: User = {
name: ''abc'',
address: {
street: ''xyz'',
},
};
O puede asignar cualquier objeto de datos (por ejemplo, recibido mediante el análisis JSON) a una interfaz (pero sus controles previos deberían afirmar que se trata de datos realmente válidos). Por lo tanto, las interfaces son muy flexibles para los datos.
Por otro lado, las clases tienen un tipo asociado en tiempo de ejecución para ellos y se genera un código. Puede verificar el tipo en tiempo de ejecución con instanceof
y hay una configuración de cadena de prototipo. Si define el User
como una clase, no será un usuario válido a menos que llame a la función de constructor. Y no puede simplemente definir cualquier tipo de datos adecuados para ser un User
. Debería crear una nueva instancia y copiar las propiedades.
Mi regla de oro personal:
- Si estoy tratando con datos puros (de diferentes fuentes) uso interfaces
- Si estoy modelando algo que tiene identidad y estado (y probablemente adjunte métodos para modificar el estado) estoy usando una clase.
También vine a Typescript desde un fondo de C # y me he preguntado lo mismo. Estaba pensando en las líneas de POCO (¿es POTO una cosa?)
Entonces, ¿para qué están diseñadas las interfaces en TypeScript?
El Manual de mecanografía parece decir que las interfaces están destinadas a " definir contratos dentro de su código".
¿Por qué la gente usa interfaces en TypeScript como usamos clases en C #?
Estoy de acuerdo con la respuesta de @ deceze aquí.
John Papa amplía el tema de las clases y las interfaces en su blog . Sugiere que las clases son las más adecuadas para "crear múltiples instancias nuevas, usar herencia, [y] objetos únicos". Entonces, basado en la intención de las interfaces de Typescript como se describe en el Manual de Typescript y la opinión de un hombre, parece que las clases no son necesarias para establecer contratos en Typescript. En cambio, debes usar interfaces. (Sus sentidos C # todavía se ofenderán).
Las interfaces se deben usar correctamente en TypeScript: ¿para establecer contratos de comportamiento o para definir las propiedades y el objeto que debería tener?
Si entiendo la pregunta, usted está preguntando si las interfaces deben establecer contratos de comportamiento o contratos de estructura . A esto, yo respondería: ambos. Las interfaces de tipo mecanografiado todavía se pueden usar de la misma manera que las interfaces se usan en C # o Java (es decir, para describir el comportamiento de una clase), pero también ofrecen la capacidad de describir la estructura de los datos.
Además, mi compañero de trabajo se apoderó de mí por usar clases en lugar de interfaces porque las interfaces no producen código en el compilador.
Ejemplo:
Este mecanografiado:
class Car implements ICar {
foo: string;
bar(): void {
}
}
interface ICar {
foo: string;
bar(): void;
}
produce este Javascript:
var Car = (function () {
function Car() {
}
Car.prototype.bar = function () {
};
return Car;
}());
Usando solo el mecanismo de deserialización nativo, no puede deserializar una instancia de una clase específica. Solo puedes deserializar en un objeto simple de JavaScript antiguo. Dichos objetos pueden adherirse a interfaces de texto mecanografiado pero no pueden ser una instancia de una clase. Si necesita tratar con datos que cruzan un límite de serialización como los datos esperados de un servicio web, use interfaces. Si necesita generar nuevas instancias de tales valores, simplemente créelos literalmente o cree una función de conveniencia que los devuelva: objetos que se adhieren a esa interfaz.
Una clase puede implementar una interfaz por sí misma, pero puede ser confusa si espera tratar tanto con instancias de clase construidas localmente como con objetos planos deserializados y reconstituidos. Nunca podrá confiar en la base de clases del objeto, por lo que no sería beneficioso definirlo también como clase para ese propósito exacto.
Tuve éxito al crear un módulo ServerProxy responsable de enviar y recibir el código de un servicio web: la llamada al servicio web y el resultado devuelto. Si está vinculando a modelos knockout o similares, puede tener una clase que encapsule el modelo orientado a la interfaz de usuario con un constructor que sepa cómo levantar un objeto simple-antiguo-javascript devuelto que cumpla con el contrato de solo interfaz del servicio web. una instancia de tu clase modelo.