Typescript sobrescribiendo un método abstracto que tiene retorno void
abstract class Base{
abstract sayHello(): void;
}
class Child extends Base{
sayHello() {
return 123;
}
}
El tipo de retorno del método abstracto es void, pero puedo implementarlo con un retorno de tipo número. No recibí ningún error del compilador. ¿Es este tipo de error en Typescript?
Especificación TS
El tipo Void, referenciado por la palabra clave void, representa la ausencia de un valor y se usa como el tipo de retorno de funciones que no devuelven ningún valor.
Respuesta #1
La razón de esto es porque el sistema de tipos en TypeScript funciona con un sistema de tipos estructural. La idea de los tipos es algo así como "contratos" donde se establecen reglas para decir que ciertos tipos son compatibles con ciertos tipos. El compilador solo dará error si se rompe el contrato. Así que, por ejemplo:
abstract class Base{
abstract sayHello(): void;
}
class Child extends Base{
sayHello(): number {
return 123;
}
}
Esto funciona porque, aunque nuestro método devuelve un número, cuando lo implementamos, todo lo que usa sayHello() y espera que devuelva void no tendrá efectos secundarios, no se rompen contratos. Esto se debe simplemente a que Microsoft estableció una regla en TypeScript que dice que el tipo de retorno void es compatible con el tipo de retorno number. La razón es, como dije antes, que no tiene efectos secundarios; todo lo que espera que sayHello() devuelva void debería seguir funcionando como se espera.
Por otro lado, si hiciera algo así:
abstract class Base{
abstract sayHello(): number;
}
class Child extends Base{
sayHello(): void {
return;
}
}
Esto generará un error, porque ahora todo lo que use sayHello() y espere que sea un número se verá afectado, el contrato de tipos se ha roto provocando un error en el compilador.
Puedes ver esto como una característica de flexibilidad en el sistema de tipos a cambio de una equivalencia de tipos accidental.
En un lenguaje como C# que utiliza un sistema de tipos nominal, no nos permitirá tener un método abstracto void que al ser implementado devuelva un string, ya que se centra en la equivalencia de tipos; los tipos deben ser iguales para ser compatibles entre sí.
¡Espero que esto ayude!
Sistema de tipos estructurales: https://es.wikipedia.org/wiki/Structural_type_system
Sistema de tipos nominal: https://es.wikipedia.org/wiki/Nominal_type_system
Compatibilidad de Tipos en Typescript: https://www.typescriptlang.org/docs/handbook/type-compatibility.html
Especificación: https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md#3114-assignment-compatibility
Respuesta #2
Cito a Ryan Cavanaugh, desarrollador principal de TS:
El valor de retorno de una referencia de función cuyo tipo de retorno es void puede ser cualquier cosa porque es legal que una función que no devuelve void sea referenciada por un tipo que sí devuelve void.
Entonces, la cuestión es sobre un tipo de función que retorna void, y puede aceptar (asignarse) un valor de función que retorna cualquier cosa.
type ReturnVoid = () => void
const foo: ReturnVoid = () => 42 // all good
Este comportamiento está especificado en la sección Compatibilidad de Asignación
M es una firma de llamada o construcción no especializada y S tiene una firma de llamada o construcción aparente N donde, [...] el tipo de resultado de M es Void, o el tipo de resultado de N es asignable al de M
Referencias :
- https://github.com/Microsoft/TypeScript/wiki/FAQ#por-qué-las-funciones-que-retornan-no-void-son-asignables-a-funciones-que-retornan-void
- https://github.com/microsoft/TypeScript/issues/21674
