Skip to content

Lección 95: Clases y Modificadores en TypeScript

🎯 Objetivo: Aprender a usar clases en TypeScript con tipado estático, modificadores de acceso y parameter properties.


TypeScript extiende las clases de JavaScript añadiendo anotaciones de tipos en propiedades y métodos.

💻 Ejemplo

class Usuario {
nombre: string;
email: string;
edad: number;
constructor(nombre: string, email: string, edad: number) {
this.nombre = nombre;
this.email = email;
this.edad = edad;
}
saludar(): string {
return `Hola, soy ${this.nombre}`;
}
}
const user = new Usuario("Ana", "ana@mail.com", 30);
console.log(user.saludar()); // "Hola, soy Ana"

TypeScript exige que las propiedades estén inicializadas en el constructor o con un valor por defecto.

class Producto {
nombre: string;
precio!: number; // ! asigna definitivamente (assertion)
categoria: string = "general"; // valor por defecto
constructor(nombre: string) {
this.nombre = nombre;
}
}

⚠️ El operador ! (definite assignment assertion) le dice a TypeScript “confía en mí, se asignará antes de usarse”. Úsalo con cuidado.


ModificadorAcceso desde
publicCualquier lugar (por defecto)
privateSolo dentro de la clase
protectedDentro de la clase y subclases
class CuentaBancaria {
private saldo: number;
constructor(saldoInicial: number) {
this.saldo = saldoInicial;
}
depositar(cantidad: number): void {
this.saldo += cantidad;
}
obtenerSaldo(): number {
return this.saldo;
}
}
const cuenta = new CuentaBancaria(1000);
cuenta.depositar(500);
console.log(cuenta.obtenerSaldo()); // 1500
// cuenta.saldo; // ❌ Error: propiedad privada

🧠 Diferencia con JS #: private de TS es solo en tiempo de compilación, no en ejecución. # de JS (campos privados reales) sí protege en runtime.

class Empleado {
protected salarioBase: number = 1000;
public nombre: string;
constructor(nombre: string) {
this.nombre = nombre;
}
}
class Desarrollador extends Empleado {
calcularSalario(): number {
return this.salarioBase * 1.5; // ✅ acceso a protected
}
}
const dev = new Desarrollador("Carlos");
// dev.salarioBase; // ❌ Error: no accesible fuera de la clase

readonly evita que una propiedad sea modificada después de la inicialización.

class Configuracion {
readonly version: string;
readonly creadoEn: Date = new Date();
constructor(version: string) {
this.version = version;
}
}
const config = new Configuracion("v2.0");
// config.version = "v3.0"; // ❌ Error: readonly
// Puede combinarse con modificadores:
class Usuario {
public readonly id: number;
private readonly token: string;
constructor(id: number, token: string) {
this.id = id;
this.token = token;
}
}

TypeScript ofrece una sintaxis compacta para declarar e inicializar propiedades directamente en el constructor.

// Versión tradicional
class UsuarioTradicional {
public nombre: string;
public email: string;
private edad: number;
constructor(nombre: string, email: string, edad: number) {
this.nombre = nombre;
this.email = email;
this.edad = edad;
}
}
// Versión con parameter properties ✨
class UsuarioCompacto {
constructor(
public nombre: string,
public email: string,
private edad: number
) {}
}
// Ambas son equivalentes
const u = new UsuarioCompacto("Ana", "ana@mail.com", 30);
console.log(u.nombre); // ✅ accesible
// console.log(u.edad); // ❌ privado

Puedes combinar con readonly:

class Articulo {
constructor(
public readonly id: string,
public titulo: string,
private _contenido: string
) {}
}

La palabra clave implements verifica que una clase cumpla con una interfaz.

interface IRepositorio {
guardar(datos: unknown): void;
obtener(id: string): unknown;
eliminar(id: string): void;
}
class RepositorioUsuarios implements IRepositorio {
guardar(datos: unknown): void {
console.log("Guardando...");
}
obtener(id: string): unknown {
return { id, nombre: "Usuario" };
}
eliminar(id: string): void {
console.log(`Eliminando ${id}`);
}
}
// ❌ Si falta un método, TypeScript lo señala
class RepositorioIncompleto implements IRepositorio {
guardar(datos: unknown): void {} // ❌ Faltan obtener y eliminar
}
interface Versiones {
version: string;
mostrarVersion(): void;
}
interface Backup {
respaldar(): void;
restaurar(): void;
}
class Servicio implements Versiones, Backup {
version: string = "1.0";
mostrarVersion(): void {
console.log(this.version);
}
respaldar(): void {
console.log("Respaldo realizado");
}
restaurar(): void {
console.log("Restauración completada");
}
}

class Temperatura {
private _celsius: number = 0;
get celsius(): number {
return this._celsius;
}
set celsius(valor: number) {
if (valor < -273.15) {
throw new Error("Temperatura por debajo del cero absoluto");
}
this._celsius = valor;
}
get fahrenheit(): number {
return (this._celsius * 9) / 5 + 32;
}
}
const temp = new Temperatura();
temp.celsius = 25;
console.log(temp.fahrenheit); // 77
// temp.celsius = -300; // ❌ Error en runtime

class Utilidades {
static readonly PI: number = 3.1416;
static formatearFecha(fecha: Date): string {
return fecha.toISOString().split("T")[0];
}
static crearId(): string {
return Math.random().toString(36).substring(2, 9);
}
}
console.log(Utilidades.PI);
console.log(Utilidades.formatearFecha(new Date()));
console.log(Utilidades.crearId());

📝 Resumen: Las clases de TypeScript añaden tipado estático a las clases de JS. Los modificadores public, private y protected controlan el acceso. Las parameter properties reducen boilerplate. implements verifica que una clase cumpla una interfaz. Getters/setters y miembros estáticos completan el ecosistema de clases tipadas.