Curso typescript nivel medio
En este módulo, profundizaremos en el manejo de objetos y clases en TypeScript. Aprenderás cómo definir y trabajar con objetos, utilizar interfaces para tipar estructuras de datos, y crear clases con propiedades, métodos y constructores. Además, exploraremos los modificadores de acceso, la herencia, el polimorfismo, y el uso de interfaces y clases abstractas para diseñar sistemas más robustos y flexibles.
4.1. Objetos y sus propiedades
En TypeScript, los objetos son colecciones de propiedades, donde cada propiedad tiene un nombre y un tipo. Al definir objetos, puedes especificar claramente qué propiedades deben existir y qué tipos de datos deben contener.
Definición básica de un objeto:
1let persona: { 2 nombre: string; 3 edad: number; 4 esEstudiante: boolean; 5} = { 6 nombre: "Ana", 7 edad: 28, 8 esEstudiante: false 9}; 10 11console.log(persona);Salida de Consola!: { nombre: 'Ana', edad: 28, esEstudiante: false }
Accediendo a las propiedades:
1let persona: { 2 nombre: string; 3 edad: number; 4 esEstudiante: boolean; 5} = { 6 nombre: "Ana", 7 edad: 28, 8 esEstudiante: false 9}; 10 11console.log(`Nombre: ${persona.nombre}`); // "Nombre: Ana" 12console.log(`Edad: ${persona.edad}`); // "Edad: 28"Salida de Consola!: Nombre: Ana Edad: 28
Actualizando propiedades:
1let persona: { 2 nombre: string; 3 edad: number; 4 esEstudiante: boolean; 5} = { 6 nombre: "Ana", 7 edad: 28, 8 esEstudiante: false 9}; 10 11persona.edad = 29; 12console.log(`Edad actualizada: ${persona.edad}`); // "Edad actualizada: 29"Salida de Consola!: Edad actualizada: 29
Tipos de propiedades opcionales:
Puedes definir propiedades opcionales utilizando el signo ?.
1let coche: { 2 marca: string; 3 modelo: string; 4 año?: number; // Propiedad opcional 5} = { 6 marca: "Toyota", 7 modelo: "Corolla" 8}; 9 10console.log(coche); // { marca: 'Toyota', modelo: 'Corolla' }Salida de Consola!: { marca: 'Toyota', modelo: 'Corolla' }
4.2. Interfaces para objetos
Las interfaces en TypeScript son estructuras que definen la forma de un objeto. Son extremadamente útiles para asegurar que los objetos cumplan con ciertos contratos, mejorando la mantenibilidad y la claridad del código.
Definiendo una interfaz:
1interface Persona { 2 nombre: string; 3 edad: number; 4 esEstudiante: boolean; 5} 6 7let alumno: Persona = { 8 nombre: "Luis", 9 edad: 22, 10 esEstudiante: true 11}; 12 13console.log(alumno);Salida de Consola!: { nombre: 'Luis', edad: 22, esEstudiante: true }
Interfaces con propiedades opcionales:
1interface Coche { 2 marca: string; 3 modelo: string; 4 año?: number; // Propiedad opcional 5} 6 7let miCoche: Coche = { 8 marca: "Honda", 9 modelo: "Civic" 10}; 11 12console.log(miCoche); // { marca: 'Honda', modelo: 'Civic' }Salida de Consola!: { marca: 'Honda', modelo: 'Civic' }
Interfaces con propiedades de solo lectura:
1interface Libro { 2 readonly titulo: string; 3 autor: string; 4} 5 6let miLibro: Libro = { 7 titulo: "1984", 8 autor: "George Orwell" 9}; 10 11miLibro.titulo = "Animal Farm"; // <--- Error.Salida de Consola!: error TS2540: Cannot assign to 'titulo' because it is a read-only property.
Interfaces con métodos:
1interface Calculadora { 2 sumar(a: number, b: number): number; 3 restar(a: number, b: number): number; 4} 5 6let calc: Calculadora = { 7 sumar: (a, b) => a + b, 8 restar: (a, b) => a - b 9}; 10 11console.log(calc.sumar(5, 3)); // 8 12console.log(calc.restar(5, 3)); // 2Salida de Consola!: 8 2
4.3. Clases: Propiedades, métodos y constructores
Las clases en TypeScript permiten crear objetos con una estructura definida, incluyendo propiedades y métodos. Además, los constructores facilitan la inicialización de las propiedades de una clase al momento de crear una instancia.
Definición básica de una clase:
1class Persona { 2 nombre: string; 3 edad: number; 4 esEstudiante: boolean; 5 6 constructor(nombre: string, edad: number, esEstudiante: boolean) { 7 this.nombre = nombre; 8 this.edad = edad; 9 this.esEstudiante = esEstudiante; 10 } 11 12 saludar(): void { 13 console.log(`Hola, me llamo ${this.nombre} y tengo ${this.edad} años.`); 14 } 15} 16 17let persona1 = new Persona("Carlos", 30, false); 18persona1.saludar(); // "Hola, me llamo Carlos y tengo 30 años."Salida de Consola!: Hola, me llamo Carlos y tengo 30 años.
Clases con métodos:
1class Coche { 2 marca: string; 3 modelo: string; 4 año: number; 5 6 constructor(marca: string, modelo: string, año: number) { 7 this.marca = marca; 8 this.modelo = modelo; 9 this.año = año; 10 } 11 12 mostrarInfo(): void { 13 console.log(`${this.marca} ${this.modelo} (${this.año})`); 14 } 15 16 actualizarAño(nuevoAño: number): void { 17 this.año = nuevoAño; 18 } 19} 20 21let miCoche = new Coche("Ford", "Mustang", 2020); 22miCoche.mostrarInfo(); // "Ford Mustang (2020)" 23miCoche.actualizarAño(2021); 24miCoche.mostrarInfo(); // "Ford Mustang (2021)"Salida de Consola!: Ford Mustang (2020) Ford Mustang (2021)
4.4. Modificadores de acceso: public, private, protected
TypeScript proporciona modificadores de acceso que controlan la visibilidad de las propiedades y métodos dentro de una clase. Esto es útil para encapsular y proteger los datos internos de una clase.
public: Las propiedades y métodos son accesibles desde cualquier parte. Es el modificador por defecto.private: Las propiedades y métodos solo son accesibles dentro de la clase donde se definen.protected: Similar aprivate, pero permite el acceso en clases derivadas (herencia).
Ejemplo de modificadores de acceso:
1class CuentaBancaria { 2 public titular: string; 3 private saldo: number; 4 protected numeroCuenta: string; 5 6 constructor(titular: string, saldo: number, numeroCuenta: string) { 7 this.titular = titular; 8 this.saldo = saldo; 9 this.numeroCuenta = numeroCuenta; 10 } 11 12 public depositar(cantidad: number): void { 13 this.saldo += cantidad; 14 console.log(`Depósito realizado. Saldo actual: ${this.saldo}`); 15 } 16 17 private retirar(cantidad: number): void { 18 if (cantidad > this.saldo) { 19 console.log("Saldo insuficiente."); 20 } else { 21 this.saldo -= cantidad; 22 console.log(`Retiro realizado. Saldo actual: ${this.saldo}`); 23 } 24 } 25 26 protected mostrarSaldo(): void { 27 console.log(`Saldo: ${this.saldo}`); 28 } 29} 30 31let cuenta = new CuentaBancaria("Laura", 1000, "123456789"); 32// Acceso público 33console.log(cuenta.titular); // "Laura" 34cuenta.depositar(500); // "Depósito realizado. Saldo actual: 1500" 35 36// Acceso privado 37console.log(cuenta.saldo); // <-- Error 38cuenta.retirar(200); // <-- Error 39 40// Acceso protegido 41console.log(cuenta.numeroCuenta); // <-- ErrorSalida de Consola!: error TS2341: Property 'saldo' is private and only accessible within class 'CuentaBancaria'. error TS2341: Property 'retirar' is private and only accessible within class 'CuentaBancaria'. error TS2445: Property 'numeroCuenta' is protected and only accessible within class 'CuentaBancaria' and its subclasses.
Herencia con modificadores de acceso:
1class CuentaBancaria { 2 public titular: string; 3 private saldo: number; 4 protected numeroCuenta: string; 5 6 constructor(titular: string, saldo: number, numeroCuenta: string) { 7 this.titular = titular; 8 this.saldo = saldo; 9 this.numeroCuenta = numeroCuenta; 10 } 11 12 public depositar(cantidad: number): void { 13 this.saldo += cantidad; 14 console.log(`Depósito realizado. Saldo actual: ${this.saldo}`); 15 } 16 17 private retirar(cantidad: number): void { 18 if (cantidad > this.saldo) { 19 console.log("Saldo insuficiente."); 20 } else { 21 this.saldo -= cantidad; 22 console.log(`Retiro realizado. Saldo actual: ${this.saldo}`); 23 } 24 } 25 26 protected mostrarSaldo(): void { 27 console.log(`Saldo: ${this.saldo}`); 28 } 29} 30 31class CuentaAhorros extends CuentaBancaria { 32 constructor(titular: string, saldo: number, numeroCuenta: string) { 33 super(titular, saldo, numeroCuenta); 34 } 35 36 public mostrarDetalleCuenta(): void { 37 // Acceso a propiedad protegida 38 console.log(`Número de Cuenta: ${this.numeroCuenta}`); 39 // Acceso a método protegido 40 this.mostrarSaldo(); 41 } 42} 43 44let cuentaAhorros = new CuentaAhorros("Pedro", 2000, "987654321"); 45cuentaAhorros.depositar(1000); // "Depósito realizado. Saldo actual: 3000" 46cuentaAhorros.mostrarDetalleCuenta(); // "Número de Cuenta: 987654321" y "Saldo: 3000"Salida de Consola!: Depósito realizado. Saldo actual: 3000 Número de Cuenta: 987654321 Saldo: 3000
4.5. Herencia y polimorfismo
La herencia permite crear nuevas clases basadas en clases existentes, reutilizando y extendiendo su funcionalidad. El polimorfismo permite que una misma interfaz sea utilizada para diferentes tipos de datos, facilitando la flexibilidad y escalabilidad del código.
Ejemplo de herencia:
1class Animal { 2 nombre: string; 3 constructor(nombre: string) { 4 this.nombre = nombre; 5 } 6 hacerSonido(): void { console.log(`${this.nombre} hace un sonido genérico.`); } 7} 8 9class Perro extends Animal { 10 constructor(nombre: string) { 11 super(nombre); 12 } 13 hacerSonido(): void { console.log(`${this.nombre} ladra.`); } 14} 15 16class Gato extends Animal { 17 constructor(nombre: string) { 18 super(nombre); 19 } 20 hacerSonido(): void { console.log(`${this.nombre} maúlla.`); } 21} 22 23let miPerro = new Perro("Rex"); 24let miGato = new Gato("Mia"); 25 26miPerro.hacerSonido(); // "Rex ladra." 27miGato.hacerSonido(); // "Mia maúlla."Salida de Consola!: Rex ladra. Mia maúlla.
Polimorfismo mediante métodos sobrescritos:
En el ejemplo anterior, hacerSonido es un método que se comporta de manera diferente dependiendo de la clase que lo implemente (Perro o Gato), demostrando el polimorfismo.
Uso de polimorfismo con arrays:
1class Animal { 2 nombre: string; 3 constructor(nombre: string) { 4 this.nombre = nombre; 5 } 6 hacerSonido(): void { console.log(`${this.nombre} hace un sonido genérico.`); } 7} 8 9class Perro extends Animal { 10 constructor(nombre: string) { 11 super(nombre); 12 } 13 hacerSonido(): void { console.log(`${this.nombre} ladra.`); } 14} 15 16class Gato extends Animal { 17 constructor(nombre: string) { 18 super(nombre); 19 } 20 hacerSonido(): void { console.log(`${this.nombre} maúlla.`); } 21} 22 23let animales: Animal[] = [new Perro("Rex"), new Gato("Mia"), new Animal("Genérico")]; 24 25animales.forEach(animal => animal.hacerSonido());Salida de Consola!: Rex ladra. Mia maúlla Genérico hace un sonido genérico.
Aquí, aunque todos los elementos son del tipo Animal, cada uno ejecuta su propia versión del método hacerSonido.
4.6. Interfaces y clases abstractas
Las clases abstractas y las interfaces son herramientas poderosas para definir contratos y estructuras que las clases deben seguir. Las clases abstractas pueden contener implementaciones parciales, mientras que las interfaces solo definen la estructura sin implementación.
Interfaces vs Clases Abstractas:
-
Interfaces:
- Solo definen la forma de un objeto (propiedades y métodos).
- No pueden contener implementación de métodos.
- Una clase puede implementar múltiples interfaces.
-
Clases Abstractas:
- Pueden contener tanto definiciones como implementaciones de métodos.
- No pueden ser instanciadas directamente.
- Una clase puede heredar de una sola clase abstracta.
Ejemplo de interfaz:
1interface Volador { 2 volar(): void; 3} 4 5interface Nadador { 6 nadar(): void; 7} 8 9class Pato implements Volador, Nadador { 10 volar(): void { 11 console.log("El pato está volando."); 12 } 13 14 nadar(): void { 15 console.log("El pato está nadando."); 16 } 17} 18 19let pato = new Pato(); 20pato.volar(); // "El pato está volando." 21pato.nadar(); // "El pato está nadando."Salida de Consola!: El pato está volando. El pato está nadando.
Ejemplo de clase abstracta:
1abstract class Vehiculo { 2 marca: string; 3 4 constructor(marca: string) { 5 this.marca = marca; 6 } 7 8 abstract arrancar(): void; // Método abstracto sin implementación 9 10 detener(): void { 11 console.log(`${this.marca} se ha detenido.`); 12 } 13} 14 15class Coche extends Vehiculo { 16 constructor(marca: string) { 17 super(marca); 18 } 19 20 arrancar(): void { 21 console.log(`${this.marca} está arrancando.`); 22 } 23} 24 25let miCoche = new Coche("BMW"); 26miCoche.arrancar(); // "BMW está arrancando." 27miCoche.detener(); // "BMW se ha detenido."Salida de Consola!: BMW está arrancando. BMW se ha detenido.
Clases abstractas con métodos implementados y abstractos:
1abstract class Figura { 2 abstract area(): number; // Método abstracto 3 4 descripcion(): void { 5 console.log("Esta es una figura geométrica."); 6 } 7} 8 9class Circulo extends Figura { 10 radio: number; 11 12 constructor(radio: number) { 13 super(); 14 this.radio = radio; 15 } 16 17 area(): number { 18 return Math.PI * this.radio * this.radio; 19 } 20} 21 22let circulo = new Circulo(5); 23circulo.descripcion(); // "Esta es una figura geométrica." 24console.log(`Área del círculo: ${circulo.area()}`); // "Área del círculo: 78.53981633974483"Salida de Consola!: Esta es una figura geométrica. Área del círculo: 78.53981633974483
Uso combinado de interfaces y clases abstractas:
1interface Volador { 2 volar(): void; 3} 4 5abstract class Ave implements Volador { 6 nombre: string; 7 8 constructor(nombre: string) { 9 this.nombre = nombre; 10 } 11 12 abstract volar(): void; 13 14 cantar(): void { 15 console.log(`${this.nombre} está cantando.`); 16 } 17} 18 19class Aguila extends Ave { 20 constructor(nombre: string) { 21 super(nombre); 22 } 23 24 volar(): void { 25 console.log(`${this.nombre} está volando alto.`); 26 } 27} 28 29let aguila = new Aguila("Águila Real"); 30aguila.cantar(); // "Águila Real está cantando." 31aguila.volar(); // "Águila Real está volando alto."Salida de Consola!: Águila Real está cantando. Águila Real está volando alto.
Resumen del Módulo 4
En este módulo, has aprendido a manejar objetos y clases en TypeScript de manera efectiva. Has visto cómo definir objetos con propiedades tipadas, utilizar interfaces para asegurar la estructura de los objetos, y crear clases con propiedades, métodos y constructores. Además, exploraste los modificadores de acceso que controlan la visibilidad de los miembros de una clase, y cómo implementar la herencia y el polimorfismo para crear sistemas más flexibles y reutilizables. Finalmente, comprendiste la diferencia y el uso conjunto de interfaces y clases abstractas para definir contratos y estructuras en tu código.
Estos conceptos son fundamentales para desarrollar aplicaciones robustas y mantenibles en TypeScript, permitiéndote aprovechar al máximo las capacidades de este poderoso lenguaje.
- Loading...
Escribe un programa en TypeScript que defina un objeto siguiendo una estructura específica.
La variable deberá llamarse
usuarioy deberá tener las siguientes propiedades:id: de tipo number, que representa el identificador único del usuario.nombre: de tipo string, que representa el nombre del usuario.perfil: un objeto anidado con las siguientes propiedades:correo: de tipo string, será opcional y representa el correo electrónico del usuario.intereses: un array de string, que representa los intereses del usuario.activo: de tipo boolean, que indica si el perfil está activo.
Requisitos del Ejercicio: Crea una interfaz llamada Usuario. Crea un objeto llamado usuario que cumpla con la estructura de la interfaz Usuario.
Loading...