Skip to content

📘 Clase 5 — Interfaces (Objetos y funciones)

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

🎯 Qué aprendí

  • Qué es una interface y cómo define la forma de un objeto.
  • Cómo tipar un objeto individual con una interfaz (let persona: Persona).
  • Cómo tipar un array de objetos con Persona[].
  • Cómo declarar propiedades opcionales con ?.
  • Cómo usar una interface para describir la firma de una función.
  • Por qué una interface es un contrato de tipos, no una clase instanciable.
  • Propiedades de solo lectura con readonly.
  • Cómo una interface extiende a otra (extends) para reusar y ampliar forma.

📖 PARTE TEÓRICA

🧱 1. ¿Qué es una interface?

Una interface le dice a TypeScript qué forma debe tener un dato: qué propiedades existen, cuáles son obligatorias y qué tipo tiene cada una.

typescript
interface Persona {
  nombre: string;
  edad: number;
  esDesarrollador: boolean;
}

💡 Piensa en la interface como un molde o contrato: cualquier objeto que diga ser Persona debe cumplir esa estructura.

👤 2. Tipar un objeto con una interface

Después de declarar la interface, puedes usarla como tipo:

typescript
let persona: Persona = {
  nombre: "Styp",
  edad: 30,
  esDesarrollador: true
};

Si falta una propiedad obligatoria, TypeScript avisa:

typescript
let personaIncompleta: Persona = {
  nombre: "Ana"
  // ❌ faltan edad y esDesarrollador
};

🧪 Entrevista: ¿Qué ventaja da una interface? Autocompletado, validación de estructura, menos errores al escribir propiedades y código más claro.

📚 3. Arrays de objetos con Interface[]

También puedes usar la interface para tipar una lista completa:

typescript
let personas: Persona[] = [
  { nombre: "Styp", edad: 30, esDesarrollador: true },
  { nombre: "Juan", edad: 25, esDesarrollador: false },
  { nombre: "María", edad: 28, esDesarrollador: true }
];

Eso significa que cada elemento del array debe cumplir la forma de Persona.

typescript
personas.push({ nombre: "Luis", edad: 26, esDesarrollador: false }); // ✅
personas.push({ nombre: "Luis" }); // ❌ faltan propiedades

❓ 4. Propiedades opcionales con ?

Una propiedad opcional se marca con ?. Puede existir o no:

typescript
interface Persona {
  nombre: string;
  edad: number;
  esDesarrollador: boolean;
  lenguajeFavorito?: string;
}

Ahora estos dos objetos son válidos:

typescript
let a: Persona = {
  nombre: "Styp",
  edad: 30,
  esDesarrollador: true,
  lenguajeFavorito: "TypeScript"
};

let b: Persona = {
  nombre: "Juan",
  edad: 25,
  esDesarrollador: false
};

💡 lenguajeFavorito?: string equivale a decir que esa propiedad puede ser string o no existir (undefined).

🔁 5. Recorrer objetos tipados

Cuando recorres un array tipado, TypeScript ya sabe qué propiedades tiene cada objeto:

typescript
personas.forEach((personaActual) => {
  console.log(personaActual.nombre);

  if (personaActual.lenguajeFavorito) {
    console.log(personaActual.lenguajeFavorito);
  }
});

📌 Dentro del if, TS entiende que lenguajeFavorito sí existe, por eso te deja usarlo sin problema.

⚙️ 6. Interfaces para funciones

Una interface no solo sirve para objetos. También puede describir la firma de una función:

typescript
interface Sumar {
  (a: number, b: number): number;
}

Eso significa: "cualquier función de tipo Sumar debe recibir dos number y devolver un number".

typescript
let sumar: Sumar = (a: number, b: number): number => {
  return a + b;
};

Si intentas romper esa firma, TS lo detecta:

typescript
let sumarMal: Sumar = (a: string, b: string) => a + b;
// ❌ no coincide con la interface

🚫 7. Interface vs clase

Una interface no se instancia. Esto no existe:

typescript
interface Persona {
  nombre: string;
}

// let p = new Persona();   // ❌ Error

La interface solo vive en TypeScript para validar tipos. Al compilar a JavaScript, desaparece.

InterfaceClase
Define forma/contrato
Se puede instanciar con new
Existe en JavaScript final
Tiene implementación real

🧪 Entrevista: ¿Entonces para qué sirve una interface si desaparece? Para ayudarte mientras escribes: valida estructura, documenta el dato y evita errores antes de ejecutar.

🔒 8. Propiedades de solo lectura (readonly)

Una propiedad marcada con readonly se puede asignar solo una vez (al crear el objeto). Después, TypeScript bloquea cualquier reasignación:

typescript
interface Persona {
  readonly nombre: string;
  readonly edad: number;
  readonly esDesarrollador: boolean;
}

let persona: Persona = { nombre: "Styp", edad: 30, esDesarrollador: true };

persona.nombre = "Otro";
// ❌ TS2540: Cannot assign to 'nombre' because it is a read-only property.
Propiedad normalreadonly
Se puede leer
Se puede reasignar después de creado el objeto
Se define al momento de crear el objeto✅ (obligatorio, no cambia luego)

💡 readonly es una protección solo en tiempo de compilación (como toda interface): en el JavaScript final no existe ningún bloqueo real, es TypeScript el que te avisa si intentas reasignar.

🧪 Entrevista: ¿readonly es lo mismo que const? No. const evita reasignar la variable completa; readonly evita reasignar una propiedad del objeto. Puedes tener const persona = {...} y aun así mutar sus propiedades si no son readonly.

🧬 9. Interfaces extendidas (extends)

Una interface puede heredar las propiedades de otra con extends, y agregar las suyas propias. Evita repetir la misma forma en varias interfaces:

typescript
interface Persona {
  readonly nombre: string;
  readonly edad: number;
  readonly esDesarrollador: boolean;
}

interface Empleado extends Persona {
  readonly puesto: string;
}

let empleado: Empleado = {
  nombre: "Styp",
  edad: 30,
  esDesarrollador: true,
  puesto: "Desarrollador Senior"
};

Empleado debe cumplir todas las propiedades de Persona más las suyas (puesto). Si falta alguna heredada, TS también avisa.

📌 extends entre interfaces es distinto a extends entre clases: aquí solo se combinan formas (no hay lógica ni constructor de por medio), es puro contrato de tipos.

🧪 Entrevista: ¿Para qué sirve extender interfaces? Para modelar jerarquías de datos sin repetir propiedades: Empleado, Cliente, Admin pueden compartir una base Persona y cada una sumar solo lo que le corresponde.


💻 PARTE PRÁCTICA

Archivos trabajados: 02-Ejercicios/Interfaces/interface.ts y interface2.ts

bash
cd 02-Ejercicios/Interfaces
tsc && node interface.js && node interface2.js

Verificado con:

  • Una interface Persona para objetos.
  • Un objeto persona tipado con esa interface.
  • Un array personas: Persona[].
  • Una propiedad opcional lenguajeFavorito?.
  • Un recorrido con forEach.
  • Una interface Sumar para una función.
  • Una interface Direccion con propiedad opcional ciudad?.
  • Una interface Persona con propiedades readonly (bloquea reasignación).
  • Una interface Empleado extends Persona (herencia entre interfaces).

Salida esperada:

text
Persona: { nombre: 'Styp', edad: 30, esDesarrollador: true, lenguajeFavorito: 'TypeScript' }
Personas: [...]
Nombre: Styp, Edad: 30, Es desarrollador: true
Lenguaje favorito: TypeScript
Nombre: Juan, Edad: 25, Es desarrollador: false
Nombre: María, Edad: 28, Es desarrollador: true
Lenguaje favorito: JavaScript
Suma: 15
Dirección: { calle: 'Calle Falsa', numero: 123, ciudad: 'Springfield' }
Persona Readonly: { nombre: 'Styp', edad: 30, esDesarrollador: true }
Empleado: { nombre: 'Styp', edad: 30, esDesarrollador: true, puesto: 'Desarrollador Senior' }

Todo verificado y corriendo. ✅


🏋️ EJERCICIOS CON SOLUCIÓN

Ejercicio 1 — Interface Producto

Crea una interface Producto con nombre, precio y stock. Luego crea un objeto válido.

Ver solución
typescript
interface Producto {
  nombre: string;
  precio: number;
  stock: number;
}

let producto: Producto = {
  nombre: "Teclado",
  precio: 120,
  stock: 8
};

console.log(producto);

Terminal esperada:

zsh · producto.ts
$ tsc producto.ts
$ node producto.js
{"nombre":"Teclado","precio":120,"stock":8}

Ejercicio 2 — Propiedad opcional

Crea una interface Usuario con nombre obligatorio y telefono opcional.

Ver solución
typescript
interface Usuario {
  nombre: string;
  telefono?: string;
}

let usuario1: Usuario = { nombre: "Ana" };
let usuario2: Usuario = { nombre: "Luis", telefono: "999888777" };

console.log(usuario1);
console.log(usuario2);

Terminal esperada:

zsh · usuario.ts
$ tsc usuario.ts
$ node usuario.js
{ nombre: 'Ana' }
{ nombre: 'Luis', telefono: '999888777' }

Ejercicio 3 — Array de objetos tipados

Crea una interface Curso y una lista con 2 cursos. Recorre la lista e imprime el nombre.

Ver solución
typescript
interface Curso {
  nombre: string;
  horas: number;
}

let cursos: Curso[] = [
  { nombre: "TypeScript", horas: 20 },
  { nombre: "Node.js", horas: 15 }
];

cursos.forEach((curso) => {
  console.log(`Curso: ${curso.nombre}`);
});

Terminal esperada:

zsh · cursos.ts
$ tsc cursos.ts
$ node cursos.js
Curso: TypeScript
Curso: Node.js

Ejercicio 4 — Interface de función

Crea una interface Multiplicar y una función arrow que la cumpla.

Ver solución
typescript
interface Multiplicar {
  (a: number, b: number): number;
}

let multiplicar: Multiplicar = (a: number, b: number): number => {
  return a * b;
};

console.log(multiplicar(4, 5)); // 20

Terminal esperada:

zsh · multiplicar.ts
$ tsc multiplicar.ts
$ node multiplicar.js
20

Ejercicio 5 — Lista de empleados con propiedad opcional

Crea una interface Empleado con nombre, area, salario y correo?. Luego crea una lista con 3 empleados e imprime un resumen de cada uno. Si tiene correo, muéstralo; si no, imprime "Sin correo registrado".

Ver solución
typescript
interface Empleado {
  nombre: string;
  area: string;
  salario: number;
  correo?: string;
}

let empleados: Empleado[] = [
  { nombre: "Ana", area: "Frontend", salario: 2500, correo: "ana@empresa.com" },
  { nombre: "Luis", area: "Backend", salario: 2800 },
  { nombre: "Marta", area: "QA", salario: 2200, correo: "marta@empresa.com" }
];

empleados.forEach((empleado) => {
  let correo = empleado.correo ? empleado.correo : "Sin correo registrado";
  console.log(
    `Nombre: ${empleado.nombre}, Área: ${empleado.area}, Salario: ${empleado.salario}, Correo: ${correo}`
  );
});

Terminal esperada:

zsh · empleados.ts
$ tsc empleados.ts
$ node empleados.js
Nombre: Ana, Área: Frontend, Salario: 2500, Correo: ana@empresa.com
Nombre: Luis, Área: Backend, Salario: 2800, Correo: Sin correo registrado
Nombre: Marta, Área: QA, Salario: 2200, Correo: marta@empresa.com

Ejercicio 6 — Filtrar objetos tipados

Crea una interface Tarea con titulo, completada y prioridad. Luego crea una lista de tareas y filtra solo las que estén completadas.

Ver solución
typescript
interface Tarea {
  titulo: string;
  completada: boolean;
  prioridad: number;
}

let tareas: Tarea[] = [
  { titulo: "Estudiar interfaces", completada: true, prioridad: 1 },
  { titulo: "Practicar arrays", completada: false, prioridad: 2 },
  { titulo: "Resolver ejercicios", completada: true, prioridad: 1 }
];

let tareasCompletadas: Tarea[] = tareas.filter((tarea) => tarea.completada);

tareasCompletadas.forEach((tarea) => {
  console.log(`Tarea completada: ${tarea.titulo}`);
});

Terminal esperada:

zsh · tareas.ts
$ tsc tareas.ts
$ node tareas.js
Tarea completada: Estudiar interfaces
Tarea completada: Resolver ejercicios

Ejercicio 7 — Interface de función con validación

Crea una interface EsMayorDeEdad para una función que reciba una edad y devuelva boolean. Después úsala para validar varias edades dentro de un array.

Ver solución
typescript
interface EsMayorDeEdad {
  (edad: number): boolean;
}

let esMayorDeEdad: EsMayorDeEdad = (edad: number): boolean => {
  return edad >= 18;
};

let edades: number[] = [15, 18, 21, 12, 30];

edades.forEach((edad) => {
  console.log(`Edad: ${edad} → ¿Es mayor? ${esMayorDeEdad(edad)}`);
});

Terminal esperada:

zsh · mayorEdad.ts
$ tsc mayorEdad.ts
$ node mayorEdad.js
Edad: 15 → ¿Es mayor? false
Edad: 18 → ¿Es mayor? true
Edad: 21 → ¿Es mayor? true
Edad: 12 → ¿Es mayor? false
Edad: 30 → ¿Es mayor? true

Ejercicio 8 — Interface combinada en un caso más real

Crea una interface Estudiante con nombre, notas: number[] y activo. Luego:

  1. Crea una lista con 3 estudiantes.
  2. Recorre la lista.
  3. Calcula el promedio de notas de cada uno.
  4. Imprime si el estudiante está activo o inactivo.
Ver solución
typescript
interface Estudiante {
  nombre: string;
  notas: number[];
  activo: boolean;
}

let estudiantes: Estudiante[] = [
  { nombre: "Carlos", notas: [15, 18, 20], activo: true },
  { nombre: "Lucía", notas: [12, 14, 16], activo: false },
  { nombre: "Pedro", notas: [19, 17, 18], activo: true }
];

estudiantes.forEach((estudiante) => {
  let suma = estudiante.notas.reduce((acc, nota) => acc + nota, 0);
  let promedio = suma / estudiante.notas.length;
  let estado = estudiante.activo ? "Activo" : "Inactivo";

  console.log(
    `Estudiante: ${estudiante.nombre}, Promedio: ${promedio.toFixed(2)}, Estado: ${estado}`
  );
});

Terminal esperada:

zsh · estudiantes.ts
$ tsc estudiantes.ts
$ node estudiantes.js
Estudiante: Carlos, Promedio: 17.67, Estado: Activo
Estudiante: Lucía, Promedio: 14.00, Estado: Inactivo
Estudiante: Pedro, Promedio: 18.00, Estado: Activo

Ejercicio 9 — readonly + extends

Crea una interface Vehiculo con readonly placa: string y readonly marca: string. Luego crea Auto extends Vehiculo que agregue puertas: number. Crea un objeto Auto válido e intenta reasignar placa (comenta la línea y explica el error que da).

Ver solución
typescript
interface Vehiculo {
  readonly placa: string;
  readonly marca: string;
}

interface Auto extends Vehiculo {
  puertas: number;
}

let auto: Auto = {
  placa: "ABC-123",
  marca: "Toyota",
  puertas: 4
};

console.log(auto);

// auto.placa = "XYZ-999";
// ❌ TS2540: Cannot assign to 'placa' because it is a read-only property.

Terminal esperada:

zsh · auto.ts
$ tsc auto.ts
$ node auto.js
{ placa: 'ABC-123', marca: 'Toyota', puertas: 4 }

🧭 ¿Para qué se usa una interface?

Las interfaces se usan para dar estructura y claridad a los datos. En la práctica sirven muchísimo cuando empiezas a manejar objetos más grandes o repetidos.

  • Para modelar objetos como Persona, Producto, Empleado o Estudiante.
  • Para tipar listas de objetos y evitar que se cuele un elemento con propiedades mal escritas.
  • Para declarar propiedades opcionales, cuando algunos datos pueden venir o no.
  • Para definir la firma de funciones, y así obligar a que reciban ciertos parámetros y devuelvan un tipo específico.
  • Para documentar mejor el código: viendo la interface entiendes rápidamente qué forma tiene el dato.
  • Para trabajar con datos de APIs, formularios, configuraciones, usuarios, productos, cursos, tareas y muchos otros casos reales.

💡 En resumen: una interface se usa cuando quieres que TypeScript te ayude a mantener el código ordenado, consistente y más fácil de entender.


❓ Preguntas y respuestas (autoevaluación)

1. ¿Qué es una interface en TypeScript?

Un contrato que define la forma de un dato: propiedades, tipos y, a veces, firmas de función.

2. ¿Cómo tipas un objeto con una interface?

let persona: Persona = { ... }.

3. ¿Cómo se tipa una lista de objetos con interface?

Con Persona[], Producto[], etc.

4. ¿Qué hace ? en una propiedad de interface?

La vuelve opcional: puede existir o faltar.

5. ¿Una interface se puede instanciar con new?

No. No tiene implementación real; solo sirve para validar tipos.

6. ¿Una interface puede describir funciones?

Sí. Puede declarar la firma de una función, por ejemplo (a: number, b: number): number.

7. ¿Qué diferencia hay entre interface y clase?

La interface describe; la clase describe y además implementa. La clase existe en JS, la interface no.

8. ¿Qué hace readonly en una propiedad?

Permite asignarla solo una vez (al crear el objeto). Reasignarla después es error de compilación (TS2540), aunque en el JS final no hay ningún bloqueo real.

9. ¿readonly es lo mismo que const?

No. const bloquea reasignar la variable completa; readonly bloquea reasignar una propiedad puntual de un objeto/interface.

10. ¿Qué hace interface B extends A?

B hereda todas las propiedades de A y puede agregar las suyas. Un objeto de tipo B debe cumplir la forma completa de A más lo nuevo que declare B.

📎 Apuntes relacionados

➡️ Siguiente

type, unions e intersecciones para modelar datos de forma más flexible.