Una clase abstracta es una clase base marcada como abstract. No se puede crear con new, y puede declarar métodos abstract sin cuerpo, que cada subclase debe implementar.
Shape comparte describe() con todas las subclases y obliga a cada una a aportar area(). El tipo Shape[] admite cualquier mezcla de subclases.
Una clase abstracta no se puede instanciar
Llamar a new sobre una clase abstracta es un error de compilación, porque el objeto tendría métodos sin implementación:
index.ts(6,11): error TS2511: Cannot create an instance of an abstract class.
Es una regla en tiempo de compilación. abstract se borra de la salida, así que el Shape generado es una clase normal de JavaScript, y código JavaScript sin tipos podría seguir llamando a new Shape(). Cuando una librería necesita una protección en tiempo de ejecución, el constructor puede comprobar new.target:
Métodos y propiedades abstractos
Una subclase debe implementar todos los miembros abstractos, o ser abstracta a su vez. Si falta uno, es el error TS2515, que dice algo como Non-abstract class 'Square' does not implement inherited abstract member area from class 'Shape', o TS2654, que los enumera todos cuando faltan varios.
Los campos, los getters y los métodos pueden ser abstractos, y pueden ser protected:
Un método abstracto no tiene cuerpo; escribirle uno es el error TS1245 (Method 'x' cannot have an implementation because it is marked abstract). Una clase abstracta no tiene por qué tener ningún miembro abstracto: abstract por sí solo impide la instanciación directa.
El patrón template method
La razón más habitual para escribir una clase abstracta: la clase base fija el orden de los pasos, y cada subclase rellena los pasos que varían.
Las clases abstractas pueden ser genéricas, como aquí, para que cada subclase elija el tipo de registro.
Tipos de constructor abstractos
typeof Shape para una clase abstracta es un constructor que no se puede llamar, así que un parámetro de tipo typeof Shape no puede hacer new c(). Cuando una función debe aceptar clases abstractas (por ejemplo para registrarlas o comprobar instanceof), típala con una firma de construcción abstracta:
abstract new acepta clases abstractas y concretas; new a secas solo acepta las concretas.
Clase abstracta frente a interfaz
| Clase abstracta | Interfaz | |
|---|---|---|
| Contiene código | sí: métodos, inicializadores de campos, constructor | no, solo tipos |
| Existe en tiempo de ejecución | sí | no, se borra |
instanceof | funciona | imposible |
| Una clase puede usar | una (extends) | muchas (implements) |
| Modificadores de acceso | private, protected, public | solo miembros públicos |
| Tipo para objetos normales | solo si no tiene miembros private/protected, y entonces instanceof falla | sí, cualquier valor con esa forma |
La regla práctica: una interfaz cuando solo necesitas describir una forma (el caso más habitual, y también funciona con objetos literales), una clase abstracta cuando las subclases comparten implementación real y quieres que el compilador las obligue a rellenar los huecos. Las dos se combinan bien: una interfaz para el contrato público y una clase abstracta como base práctica que la implementa. Consulta interface frente a type para la otra comparación habitual.
Errores habituales
- Usar una clase abstracta donde bastaría una interfaz. Si la clase base no tiene código, es una interfaz con peso extra en tiempo de ejecución y el límite de herencia única.
- Intentar
static abstract. No está permitido. Los miembros estáticos no pueden ser abstractos. - Esperar un error en tiempo de ejecución al hacer
newsobre una clase abstracta. No lo hay, salvo que escribas la comprobación denew.target. - Llamar a un método abstracto desde el constructor base. Se ejecuta antes de que se inicialicen los campos de la subclase, así que la implementación los ve como
undefined.
Preguntas frecuentes
¿Qué es una clase abstracta en TypeScript?
Una clase marcada como abstract que no se puede crear con new y que puede declarar miembros abstract sin implementación. Las subclases deben implementar todos los miembros abstractos. Es una forma de compartir código y exigir una forma al mismo tiempo.
¿Qué diferencia hay entre una clase abstracta y una interfaz en TypeScript?
Una clase abstracta puede contener código real (métodos implementados, campos con valores iniciales, un constructor) y existe en tiempo de ejecución, así que instanceof funciona con ella; una clase solo puede extender una. Una interfaz no tiene código, se borra al compilar, y una clase puede implementar muchas. Usa una clase abstracta para compartir comportamiento y una interfaz para describir una forma.
¿Puede una clase abstracta tener constructor en TypeScript?
Sí. Se ejecuta cuando una subclase llama a super(...). Lo que sigue sin poder hacerse es llamar a new sobre la propia clase abstracta (TS2511: Cannot create an instance of an abstract class).
¿Pueden ser abstractos los métodos estáticos en TypeScript?
No. abstract solo se aplica a miembros de instancia; static abstract es el error TS1243 ('static' modifier cannot be used with 'abstract' modifier). Para exigir una forma estática, tipa el constructor con una interfaz que incluya el miembro estático y comprueba la clase contra ella.
¿Se aplica una clase abstracta en tiempo de ejecución?
No. abstract se borra, así que la clase compilada es JavaScript normal y el código JavaScript normal podría seguir llamando a new sobre ella. Si eso importa, comprueba new.target en el constructor y lanza un error.