Las clases en TypeScript son clases de JavaScript con un sistema de tipos encima. El resultado es una sintaxis más parecida a otros lenguajes orientados a objetos como Java o C#, pero compilando a JavaScript estándar. Esta guía cubre el tipado de propiedades, los modificadores de acceso, las clases abstractas y las diferencias entre el private de TypeScript y el #private nativo de JavaScript.
Propiedades tipadas y el shorthand de constructor
En TypeScript puedes declarar las propiedades de una clase fuera del constructor, lo que mejora la legibilidad y permite que TypeScript compruebe que todas se inicializan:
class Producto {
id: number;
nombre: string;
precio: number;
enStock: boolean = true; // valor por defecto
constructor(id: number, nombre: string, precio: number) {
this.id = id;
this.nombre = nombre;
this.precio = precio;
}
}
El shorthand de constructor elimina la repetición declarando las propiedades directamente en los parámetros con un modificador de acceso:
class Producto {
enStock: boolean = true;
constructor(
public id: number,
public nombre: string,
public precio: number
) {}
}
Modificadores de acceso: public, private y protected
Por defecto, todas las propiedades y métodos son public. private restringe el acceso al interior de la clase. protected permite acceso también desde subclases:
class CuentaBancaria {
private saldo: number;
protected titular: string;
constructor(titular: string, saldoInicial: number) {
this.titular = titular;
this.saldo = saldoInicial;
}
depositar(cantidad: number): void {
if (cantidad <= 0) throw new Error("Cantidad inválida");
this.saldo += cantidad;
}
getSaldo(): number {
return this.saldo;
}
}
class CuentaPremium extends CuentaBancaria {
mostrarResumen(): string {
return `Titular: ${this.titular}`; // protected: accesible
// this.saldo ? Error: propiedad privada de CuentaBancaria
}
}
readonly
readonly impide que una propiedad se reasigne fuera del constructor. Es útil para valores que se deben inicializar una vez y no cambiar:
class Configuracion {
readonly apiUrl: string;
readonly timeout: number;
constructor(apiUrl: string, timeout = 5000) {
this.apiUrl = apiUrl;
this.timeout = timeout;
}
}
const config = new Configuracion("https://api.ejemplo.com");
config.apiUrl = "otro"; // Error: no se puede asignar a una propiedad readonly
Clases abstractas
Una clase abstracta define una interfaz que sus subclases deben implementar. No se puede instanciar directamente. Los métodos abstractos solo tienen declaración, no implementación:
abstract class Forma {
abstract area(): number;
abstract perimetro(): number;
describir(): string {
return `Área: ${this.area().toFixed(2)}, Perímetro: ${this.perimetro().toFixed(2)}`;
}
}
class Circulo extends Forma {
constructor(private radio: number) { super(); }
area(): number { return Math.PI * this.radio ** 2; }
perimetro(): number { return 2 * Math.PI * this.radio; }
}
class Rectangulo extends Forma {
constructor(private ancho: number, private alto: number) { super(); }
area(): number { return this.ancho * this.alto; }
perimetro(): number { return 2 * (this.ancho + this.alto); }
}
implements: clases que cumplen interfaces
implements obliga a una clase a cumplir un contrato definido por una interfaz. No añade código en tiempo de ejecución, solo comprobación en tiempo de compilación:
interface Serializable {
serializar(): string;
deserializar(datos: string): void;
}
class Sesion implements Serializable {
private datos: Record = {};
serializar(): string {
return JSON.stringify(this.datos);
}
deserializar(datos: string): void {
this.datos = JSON.parse(datos);
}
}
private de TypeScript vs #private de JavaScript
El private de TypeScript es una comprobación solo en tiempo de compilación: en el JavaScript resultante, la propiedad es accesible. El #private de JavaScript (soportado desde TypeScript 4.3) es un private real en tiempo de ejecución, gestionado por la especificación del lenguaje:
class Ejemplo {
private tsPrivado = 1; // accesible en JS si se fuerza
#jsPrivado = 2; // error en JS aunque se intente: sintaxis inaccesible
obtenerJs(): number { return this.#jsPrivado; }
}
const e = new Ejemplo();
// (e as any).tsPrivado ? funciona en tiempo de ejecución
// (e as any).#jsPrivado ? SyntaxError en tiempo de ejecución
Para encapsulación real en tiempo de ejecución, usa #. Para encapsulación a nivel de tipado dentro de un equipo que controla todo el código TypeScript, private es suficiente y más legible.
Imagen: Pexels / Stanislav Kondratiev
