Skip to content

📘 Clase 8 — Herencia (extends)

TypeScript · 2026-07-12 · Carpeta: 02-Ejercicios/Herencia ⬅️ Volver al índice de clases

🎯 Qué aprendí

  • Qué significa extender una clase en TypeScript.
  • Cómo usar extends para heredar propiedades y métodos.
  • Cómo crear una clase hija Empleado a partir de una clase base Persona.
  • Para qué sirve super(...) dentro del constructor.
  • Cómo sobrescribir métodos heredados.
  • Cómo funciona la compilación en cascada cuando un archivo importa a otro.
  • Qué archivos .js debes llevar a producción para que todo funcione.

📖 PARTE TEÓRICA

🧱 1. ¿Qué significa extender una clase?

Extender una clase en TypeScript te permite crear una nueva clase que hereda propiedades y métodos de otra sin modificar la original.

Con la palabra reservada extends puedes ampliar una clase base como Persona y construir variantes especializadas como Empleado, sumando nuevas propiedades y comportamientos.

💡 Es una técnica clave para reutilizar código en proyectos orientados a objetos.

🧬 2. ¿Qué hace extends?

La idea es simple: ya tienes una clase Persona que define ciertos datos y comportamientos, y quieres una versión más específica que además tenga su propia lógica.

typescript
export class Empleado extends Persona {
  // ...
}

extends le dice a TypeScript:

  • “esta clase nueva hereda lo de la clase base”
  • “no dupliques lo que ya existe”
  • “solo agrega lo nuevo”

🧪 ¿Qué hace la palabra clave extends? Le indica a TypeScript que la nueva clase hereda propiedades y métodos de la clase base.

👤 3. Clase base Persona

La clase base real usada en tu ejercicio fue esta:

typescript
export class Persona {
  public name: string;
  protected age: number;
  private isDeveloper: boolean;

  constructor(name: string, age: number, isDeveloper: boolean) {
    this.name = name;
    this.age = age;
    this.isDeveloper = isDeveloper;
  }

  public saludar(): string {
    return `Hola, mi nombre es ${this.name}, tengo ${this.age} años`;
  }

  protected obtenerEdad(): number {
    return this.age;
  }

  private esDesarrollador(): boolean {
    return this.isDeveloper;
  }
}

Esta clase ya tiene:

  • una propiedad pública: name
  • una protegida: age
  • una privada: isDeveloper
  • un método público: saludar()

🧑‍💼 4. Clase hija Empleado

Luego creaste una clase Empleado que extiende de Persona:

typescript
import { Persona } from "./clase.js";

export class Empleado extends Persona {
  public position: string;

  constructor(name: string, age: number, isDeveloper: boolean, puesto: string) {
    super(name, age, isDeveloper);
    this.position = puesto;
  }

  public saludar(): string {
    return `Hola, trabajo como ${this.position}`;
  }

  public ObtenerInfo(): string {
    return `Hola, mi nombre es ${this.name}, tengo ${this.obtenerEdad()} años y trabajo como ${this.position}`;
  }
}

Lo importante aquí:

  • Empleado hereda de Persona
  • agrega una propiedad nueva: position
  • usa super(...) para inicializar la parte heredada
  • sobrescribe saludar()
  • aprovecha name y obtenerEdad() desde la clase base

🚚 5. ¿Por qué el constructor recibe todas las propiedades?

Porque la clase hija debe pasarle a la clase padre los valores que esta necesita.

typescript
constructor(name: string, age: number, isDeveloper: boolean, puesto: string) {
  super(name, age, isDeveloper);
  this.position = puesto;
}

Aquí pasa esto:

  • name, age e isDeveloper van a Persona
  • puesto se guarda en Empleado

💡 super(...) llama al constructor de la clase padre. Sin eso, la parte heredada no se inicializa correctamente.

🔁 6. Sobrescribir métodos heredados

Sí, puedes redefinir un método que ya existe en la clase base.

La clase Persona tenía:

typescript
public saludar(): string {
  return `Hola, mi nombre es ${this.name}, tengo ${this.age} años`;
}

Pero en Empleado escribiste:

typescript
public saludar(): string {
  return `Hola, trabajo como ${this.position}`;
}

Eso significa que cuando llamas:

typescript
empleado.saludar();

se ejecuta la versión de Empleado, no la de Persona.

🧪 ¿Puedo sobrescribir métodos heredados? Sí. Si la clase hija define un método con el mismo nombre, reemplaza al heredado al ejecutarse desde una instancia hija.

📦 7. ¿Por qué usar export?

Sin export, la clase no se puede importar desde otros archivos.

typescript
export class Empleado extends Persona {
  // ...
}

Y luego:

typescript
import { Empleado } from "./empleado.js";

⚠️ Si olvidas export, rompes la cadena de reutilización entre archivos.

🧩 8. Instanciar y probar la clase hija

En tu main.ts hiciste esto:

typescript
import { Empleado } from "./empleado.js";

let empleado = new Empleado("Juan", 30, true, "Desarrollador");

console.log(empleado.saludar());
console.log(empleado.ObtenerInfo());
console.log("Nombre:", empleado.name);

Fíjate en la ventaja:

  • llamas a métodos nuevos: ObtenerInfo()
  • llamas a métodos sobrescritos: saludar()
  • accedes a propiedades heredadas públicas: name

Eso es justamente la herencia: el objeto hijo conserva lo útil de la base y añade lo suyo.

🧠 9. Compilación en cascada

Cuando compilas con:

bash
tsc main.ts

TypeScript sigue la cadena de dependencias automáticamente:

  1. compila main.ts
  2. detecta que importa empleado.ts
  3. detecta que empleado.ts importa clase.ts
  4. genera todos los .js necesarios

Es un efecto dominó.

💡 No necesitas compilar manualmente uno por uno si la cadena de imports está bien hecha.

🖼️ Esquema visual

La siguiente imagen resume la idea de herencia y el efecto cascada al compilar:

Explicación de herencia

🚨 ¿Qué debes tener en cuenta en producción?

Aunque en desarrollo te baste con compilar desde main.ts, en producción debes asegurarte de incluir todos los .js generados:

  • main.js
  • Empleado.js o empleado.js según tu flujo real
  • clase.js

Si falta uno, las referencias se rompen y Node no encuentra los módulos.

⚠️ La compilación en cascada te ahorra trabajo local, pero el resultado final debe estar completo.


💻 PARTE PRÁCTICA

Archivos trabajados:

  • 02-Ejercicios/Herencia/clase.ts
  • 02-Ejercicios/Herencia/empleado.ts
  • 02-Ejercicios/Herencia/main.ts
bash
cd 02-Ejercicios/Herencia
tsc main.ts && node main.js

Salida real verificada:

text
Hola,  trabajo como Desarrollador
Hola, mi nombre es Juan, tengo 30 años y trabajo como Desarrollador
Nombre: Juan

Todo verificado y corriendo. ✅


🏋️ EJERCICIOS CON SOLUCIÓN

💡 Desde aquí ya empezamos a practicar herencia con más seriedad. Algunos ejercicios usan un solo archivo y otros te obligan a separar la clase base, la clase hija y el archivo principal. Eso es intencional: así entrenas el flujo real de trabajo con varias páginas y varios módulos.

Ejercicio 1 — Animal y Perro

Crea una clase Animal con nombre y un método hacerSonido(). Luego crea una clase Perro extends Animal que sobrescriba hacerSonido().

Ver solución
typescript
class Animal {
  public nombre: string;

  constructor(nombre: string) {
    this.nombre = nombre;
  }

  public hacerSonido(): string {
    return "Sonido genérico";
  }
}

class Perro extends Animal {
  public hacerSonido(): string {
    return "Guau guau";
  }
}

const perro = new Perro("Max");
console.log(perro.nombre);
console.log(perro.hacerSonido());

Terminal esperada:

zsh · perro.ts
$ tsc perro.ts
$ node perro.js
Max
Guau guau

Ejercicio 2 — Vehiculo y Auto

Crea una clase base Vehiculo con marca y un método describir(). Luego crea Auto que herede de Vehiculo y agregue puertas.

Ver solución
typescript
class Vehiculo {
  public marca: string;

  constructor(marca: string) {
    this.marca = marca;
  }

  public describir(): string {
    return `Marca: ${this.marca}`;
  }
}

class Auto extends Vehiculo {
  public puertas: number;

  constructor(marca: string, puertas: number) {
    super(marca);
    this.puertas = puertas;
  }

  public obtenerInfo(): string {
    return `${this.describir()}, Puertas: ${this.puertas}`;
  }
}

const auto = new Auto("Toyota", 4);
console.log(auto.obtenerInfo());

Terminal esperada:

zsh · auto.ts
$ tsc auto.ts
$ node auto.js
Marca: Toyota, Puertas: 4

Ejercicio 3 — PersonaBase y Alumno

Crea una clase PersonaBase con nombre y edad. Luego crea Alumno que herede y agregue curso. Imprime un resumen con todos los datos.

Ver solución
typescript
class PersonaBase {
  public nombre: string;
  public edad: number;

  constructor(nombre: string, edad: number) {
    this.nombre = nombre;
    this.edad = edad;
  }
}

class Alumno extends PersonaBase {
  public curso: string;

  constructor(nombre: string, edad: number, curso: string) {
    super(nombre, edad);
    this.curso = curso;
  }

  public resumen(): string {
    return `${this.nombre} tiene ${this.edad} años y estudia ${this.curso}`;
  }
}

const alumno = new Alumno("Lucía", 21, "TypeScript");
console.log(alumno.resumen());

Terminal esperada:

zsh · alumno.ts
$ tsc alumno.ts
$ node alumno.js
Lucía tiene 21 años y estudia TypeScript

Ejercicio 4 — Reutilizar método heredado

Crea una clase EmpleadoBase con un método presentarse(). Luego crea Gerente que herede de ella y agregue un método areaACargo().

Ver solución
typescript
class EmpleadoBase {
  public nombre: string;

  constructor(nombre: string) {
    this.nombre = nombre;
  }

  public presentarse(): string {
    return `Hola, soy ${this.nombre}`;
  }
}

class Gerente extends EmpleadoBase {
  public area: string;

  constructor(nombre: string, area: string) {
    super(nombre);
    this.area = area;
  }

  public areaACargo(): string {
    return `${this.presentarse()} y estoy a cargo de ${this.area}`;
  }
}

const gerente = new Gerente("Carlos", "Tecnología");
console.log(gerente.areaACargo());

Terminal esperada:

zsh · gerente.ts
$ tsc gerente.ts
$ node gerente.js
Hola, soy Carlos y estoy a cargo de Tecnología

Ejercicio 5 — Sobrescribir describir() en Rectangulo

Crea una clase Figura con un método describir(). Luego crea Rectangulo que herede de ella, agregue ancho y alto, y sobrescriba describir() mostrando además el área.

Ver solución
typescript
class Figura {
  public nombre: string;

  constructor(nombre: string) {
    this.nombre = nombre;
  }

  public describir(): string {
    return `Figura: ${this.nombre}`;
  }
}

class Rectangulo extends Figura {
  public ancho: number;
  public alto: number;

  constructor(ancho: number, alto: number) {
    super("Rectángulo");
    this.ancho = ancho;
    this.alto = alto;
  }

  public calcularArea(): number {
    return this.ancho * this.alto;
  }

  public describir(): string {
    return `${super.describir()}, área: ${this.calcularArea()}`;
  }
}

const rectangulo = new Rectangulo(8, 5);
console.log(rectangulo.describir());

Terminal esperada:

zsh · rectangulo.ts
$ tsc rectangulo.ts
$ node rectangulo.js
Figura: Rectángulo, área: 40

Ejercicio 6 — Cuenta y CuentaPremium

Crea una clase Cuenta con titular y saldo. Luego crea CuentaPremium que herede y agregue un método retirar() con validación de saldo.

Ver solución
typescript
class Cuenta {
  public titular: string;
  public saldo: number;

  constructor(titular: string, saldo: number) {
    this.titular = titular;
    this.saldo = saldo;
  }

  public mostrarSaldo(): string {
    return `${this.titular} tiene ${this.saldo} soles`;
  }
}

class CuentaPremium extends Cuenta {
  public retirar(monto: number): string {
    if (monto > this.saldo) {
      return "Saldo insuficiente";
    }

    this.saldo -= monto;
    return `Retiro exitoso. Nuevo saldo: ${this.saldo}`;
  }
}

const cuenta = new CuentaPremium("Ana", 900);
console.log(cuenta.mostrarSaldo());
console.log(cuenta.retirar(250));

Terminal esperada:

zsh · cuenta-premium.ts
$ tsc cuenta-premium.ts
$ node cuenta-premium.js
Ana tiene 900 soles
Retiro exitoso. Nuevo saldo: 650

Ejercicio 7 — Producto y ProductoConDescuento

Crea una clase Producto con nombre y precio. Luego crea ProductoConDescuento que herede y agregue porcentaje. Crea un método para devolver el precio final.

Ver solución
typescript
class Producto {
  public nombre: string;
  public precio: number;

  constructor(nombre: string, precio: number) {
    this.nombre = nombre;
    this.precio = precio;
  }

  public mostrarInfo(): string {
    return `${this.nombre}: S/ ${this.precio}`;
  }
}

class ProductoConDescuento extends Producto {
  public porcentaje: number;

  constructor(nombre: string, precio: number, porcentaje: number) {
    super(nombre, precio);
    this.porcentaje = porcentaje;
  }

  public calcularPrecioFinal(): number {
    return this.precio - (this.precio * this.porcentaje) / 100;
  }
}

const producto = new ProductoConDescuento("Teclado", 150, 20);
console.log(producto.mostrarInfo());
console.log(`Precio final: S/ ${producto.calcularPrecioFinal()}`);

Terminal esperada:

zsh · producto-descuento.ts
$ tsc producto-descuento.ts
$ node producto-descuento.js
Teclado: S/ 150
Precio final: S/ 120

Ejercicio 8 — Separar Persona y Empleado en varios archivos

Ahora sí practica con varias páginas. Crea persona.ts, empleado.ts y main.ts. La clase Empleado debe extender a Persona y agregar puesto.

Ver solución
typescript
// persona.ts
export class Persona {
  public nombre: string;

  constructor(nombre: string) {
    this.nombre = nombre;
  }

  public saludar(): string {
    return `Hola, soy ${this.nombre}`;
  }
}
typescript
// empleado.ts
import { Persona } from "./persona.js";

export class Empleado extends Persona {
  public puesto: string;

  constructor(nombre: string, puesto: string) {
    super(nombre);
    this.puesto = puesto;
  }

  public describirTrabajo(): string {
    return `${this.saludar()} y trabajo como ${this.puesto}`;
  }
}
typescript
// main.ts
import { Empleado } from "./empleado.js";

const empleado = new Empleado("Juan", "Backend Developer");
console.log(empleado.describirTrabajo());

Terminal esperada:

zsh · main.ts
$ tsc main.ts
$ node main.js
Hola, soy Juan y trabajo como Backend Developer

Ejercicio 9 — Usuario y Administrador en archivos separados

Crea una clase base Usuario y una clase hija Administrador. La hija debe agregar nivelAcceso y un método que muestre su rol.

Ver solución
typescript
// usuario.ts
export class Usuario {
  public nombre: string;

  constructor(nombre: string) {
    this.nombre = nombre;
  }

  public presentacion(): string {
    return `Usuario: ${this.nombre}`;
  }
}
typescript
// administrador.ts
import { Usuario } from "./usuario.js";

export class Administrador extends Usuario {
  public nivelAcceso: string;

  constructor(nombre: string, nivelAcceso: string) {
    super(nombre);
    this.nivelAcceso = nivelAcceso;
  }

  public mostrarRol(): string {
    return `${this.presentacion()} | Acceso: ${this.nivelAcceso}`;
  }
}
typescript
// main.ts
import { Administrador } from "./administrador.js";

const admin = new Administrador("María", "Total");
console.log(admin.mostrarRol());

Terminal esperada:

zsh · main.ts
$ tsc main.ts
$ node main.js
Usuario: María | Acceso: Total

Ejercicio 10 — Dispositivo y Laptop

Crea una clase Dispositivo con marca y modelo. Luego crea Laptop que herede y agregue ram. Debe mostrar un resumen técnico completo.

Ver solución
typescript
class Dispositivo {
  public marca: string;
  public modelo: string;

  constructor(marca: string, modelo: string) {
    this.marca = marca;
    this.modelo = modelo;
  }

  public datosBase(): string {
    return `${this.marca} ${this.modelo}`;
  }
}

class Laptop extends Dispositivo {
  public ram: number;

  constructor(marca: string, modelo: string, ram: number) {
    super(marca, modelo);
    this.ram = ram;
  }

  public fichaTecnica(): string {
    return `${this.datosBase()} con ${this.ram} GB de RAM`;
  }
}

const laptop = new Laptop("Lenovo", "ThinkPad", 16);
console.log(laptop.fichaTecnica());

Terminal esperada:

zsh · laptop.ts
$ tsc laptop.ts
$ node laptop.js
Lenovo ThinkPad con 16 GB de RAM

Ejercicio 11 — Empleado y Vendedor con comisión

Crea una clase base Empleado con nombre y sueldoBase. Luego crea Vendedor que herede y agregue ventas. Debe calcular su pago total con comisión.

Ver solución
typescript
class Empleado {
  public nombre: string;
  public sueldoBase: number;

  constructor(nombre: string, sueldoBase: number) {
    this.nombre = nombre;
    this.sueldoBase = sueldoBase;
  }
}

class Vendedor extends Empleado {
  public ventas: number;

  constructor(nombre: string, sueldoBase: number, ventas: number) {
    super(nombre, sueldoBase);
    this.ventas = ventas;
  }

  public calcularPagoTotal(): number {
    return this.sueldoBase + this.ventas * 0.1;
  }

  public resumen(): string {
    return `${this.nombre} cobrará ${this.calcularPagoTotal()} soles`;
  }
}

const vendedor = new Vendedor("Elena", 1400, 5000);
console.log(vendedor.resumen());

Terminal esperada:

zsh · vendedor.ts
$ tsc vendedor.ts
$ node vendedor.js
Elena cobrará 1900 soles

Ejercicio 12 — CursoOnline extiende Curso

Crea una clase base Curso con titulo y duracion. Luego crea CursoOnline que herede y agregue plataforma. Usa super para reutilizar la descripción base.

Ver solución
typescript
class Curso {
  public titulo: string;
  public duracion: number;

  constructor(titulo: string, duracion: number) {
    this.titulo = titulo;
    this.duracion = duracion;
  }

  public describir(): string {
    return `${this.titulo} dura ${this.duracion} horas`;
  }
}

class CursoOnline extends Curso {
  public plataforma: string;

  constructor(titulo: string, duracion: number, plataforma: string) {
    super(titulo, duracion);
    this.plataforma = plataforma;
  }

  public describir(): string {
    return `${super.describir()} y se dicta en ${this.plataforma}`;
  }
}

const curso = new CursoOnline("POO con TypeScript", 12, "Platzi");
console.log(curso.describir());

Terminal esperada:

zsh · curso-online.ts
$ tsc curso-online.ts
$ node curso-online.js
POO con TypeScript dura 12 horas y se dicta en Platzi

Ejercicio 13 — Mini práctica real con tres archivos y dos métodos heredados

Practica un caso más cercano a proyecto real. Crea persona.ts, empleado.ts y main.ts. La clase Empleado debe heredar de Persona, agregar puesto, sobrescribir saludar() y tener además obtenerInfo().

Ver solución
typescript
// persona.ts
export class Persona {
  public nombre: string;
  public edad: number;

  constructor(nombre: string, edad: number) {
    this.nombre = nombre;
    this.edad = edad;
  }

  public saludar(): string {
    return `Hola, soy ${this.nombre}`;
  }
}
typescript
// empleado.ts
import { Persona } from "./persona.js";

export class Empleado extends Persona {
  public puesto: string;

  constructor(nombre: string, edad: number, puesto: string) {
    super(nombre, edad);
    this.puesto = puesto;
  }

  public saludar(): string {
    return `Hola, trabajo como ${this.puesto}`;
  }

  public obtenerInfo(): string {
    return `${this.nombre} tiene ${this.edad} años y trabaja como ${this.puesto}`;
  }
}
typescript
// main.ts
import { Empleado } from "./empleado.js";

const empleado = new Empleado("Juan", 30, "Frontend Developer");

console.log(empleado.saludar());
console.log(empleado.obtenerInfo());
console.log(`Nombre heredado: ${empleado.nombre}`);

Terminal esperada:

zsh · main.ts
$ tsc main.ts
$ node main.js
Hola, trabajo como Frontend Developer
Juan tiene 30 años y trabaja como Frontend Developer
Nombre heredado: Juan

🧭 ¿Para qué sirve la herencia?

La herencia se usa para reutilizar código sin duplicarlo.

  • Cuando varias clases comparten una base común
  • Cuando quieres especializar una clase sin reescribir todo
  • Cuando necesitas heredar propiedades y métodos ya probados
  • Cuando una clase hija añade comportamiento nuevo encima de una clase padre

💡 En resumen: extends te deja construir clases más específicas partiendo de una base ya hecha.


❓ Preguntas y respuestas (autoevaluación)

1. ¿Qué hace extends?

Hace que una clase herede propiedades y métodos de otra clase.

2. ¿Para qué sirve super(...)?

Para llamar al constructor de la clase padre e inicializar su parte heredada.

3. ¿Una clase hija puede agregar propiedades nuevas?

Sí. Hereda lo anterior y además puede definir sus propias propiedades y métodos.

4. ¿Puedo sobrescribir un método heredado?

Sí. Si defines el mismo método en la clase hija, su versión reemplaza a la de la base.

5. ¿Por qué tsc main.ts compila también otros archivos?

Porque TypeScript sigue automáticamente la cadena de imports y compila las dependencias.

📎 Apuntes relacionados

➡️ Siguiente

Polimorfismo y composición o seguir profundizando en POO.