Clase 17 - Introducción TypeScript
1. 🌟 Introducción a TypeScript
TypeScript es un lenguaje de programación desarrollado por Microsoft que extiende JavaScript añadiendo tipado estático opcional. TypeScript se compila a JavaScript, permitiendo que el código se ejecute en cualquier navegador o entorno que soporte JavaScript.
🤔 ¿Por qué usar TypeScript?
- Detección de errores temprana: Los errores se detectan durante el desarrollo, antes de la ejecución.
- Mejor soporte de IDE: Autocompletado, navegación de código y refactorización.
- Documentación integrada: Los tipos sirven como documentación viva del código.
- Mayor escalabilidad: Facilita el mantenimiento de bases de código grandes.
- Integración con frameworks modernos: Angular, React, Vue, Astro y otros tienen excelente soporte para TypeScript.
⚙️ Instalación y configuración
Para inicializar un proyecto con TypeScript (una de las tantas formas de hacerlo):
mkdir mi-proyecto-tscd mi-proyecto-tspnpm initpnpm add -D typescripttsc --initEl comando tsc --init genera un archivo tsconfig.json con la configuración básica para TypeScript.
2. 📊 Tipos básicos
TypeScript nos permite definir tipos para las variables, parámetros de funciones y valores de retorno.
🧩 Tipos primitivos
// Declaración de variables con tiposlet name: string = "Santiago";let age: number = 28;let isValid: boolean = true;let notDefined: undefined = undefined;let justNull: null = null;let aSymbol: symbol = Symbol("simbolo");let anyValue: any = "puedo ser cualquier cosa";
// Arrayslet numbers: number[] = [1, 2, 3, 4, 5];let names: Array<string> = ["Ana", "Juan", "Pedro"];
// Tuplas (arrays con número fijo de elementos con tipos específicos)let userInfo: [string, number] = ["Santiago", 28];🃏 El tipo any y unknown
// any - evita el chequeo de tipos (usar con moderación o no usarlo)let anyData: any = 4;anyData = "a string"; // una cadenaanyData = false;
// unknown - similar a any pero más segurolet unknownValue: unknown = 10;unknownValue = "hello"; // holaunknownValue = true;
// Necesitas verificar el tipo antes de operaciones específicasif (typeof unknownValue === "string") { console.log(unknownValue.toUpperCase());}
// Diferencia entre any y unknownlet anyVar: any = "I can do anything";let result = anyVar.toUpperCase(); // No hay error - pero podría fallar en tiempo de ejecución
let unknownVar: unknown = "I need type checking";// let result2 = unknownVar.toUpperCase(); // Error - TypeScript no permite esto// La forma correcta:if (typeof unknownVar === "string") { let result2 = unknownVar.toUpperCase(); // Ahora es seguro⚠️ Buena práctica: Evita usar
anysiempre que sea posible, ya que elimina los beneficios del sistema de tipos de TypeScript. Usaunknowncomo alternativa más segura cuando no conoces el tipo de un valor, ya que te obliga a realizar comprobaciones de tipo antes de operar con él.
👉 Type Assertions (Aserciones de tipo)
// Aserciones de tipolet someValue: unknown = "This is a string"; // Esto es una cadena
// Método 1: usando 'as'let stringLength: number = (someValue as string).length;
// Método 2: usando sintaxis de ángulos (menos común, no funciona en JSX - React)let stringLength2: number = (<string>someValue).length;
// Ejemplo del mundo real - Manipulación del DOMconst input = document.getElementById('my-input') as HTMLInputElement;// Ahora podemos usar input.value, ya que TypeScript sabe que es un elemento input
// Operador de aserción no nulo (!)function validateUser(user: string | null) { // El ! le dice a TypeScript que estamos seguros de que user no es null const username: string = user!; return username.toUpperCase();}3. 📝 Interfaces
Las interfaces en TypeScript definen contratos en tu código y proporcionan tipos explícitos.
// Definición de una interfazinterface User { name: string; age: number; email?: string; // Propiedad opcional con ? readonly id: number; // Propiedad de solo lectura}
// Uso de la interfazconst user1: User = { name: "Carlos", age: 28, id: 1};
// user1.id = 2; // Error: no se puede modificar una propiedad readonly
// Extendiendo interfacesinterface Employee extends User { position: string; salary: number;}
const employee1: Employee = { nombre: "Laura", edad: 35, id: 2, puesto: "Desarrolladora", salario: 3500};🔄 Interfaces para funciones
// Interfaz para funcionesinterface GreetingFunction { (name: string, hour?: number): string;}
const greetPeople: GreetingFunction = (name, hour = 0) => { if (hour < 12) return `Good morning, ${name}!`; else return `Good afternoon, ${name}!`;};
console.log(greetPeople("Maria", 14)); // Good afternoon, Maria!
// Ejemplo más complejo de interfaz de funcióninterface MathOperation { (x: number, y: number): number;}
const add: MathOperation = (a, b) => a + b;const subtract: MathOperation = (a, b) => a - b;
console.log(add(5, 3)); // 8console.log(subtract(5, 3)); // 2
// Interfaz con callbackinterface FetchUserCallback { (error: Error | null, user?: { id: number, name: string }): void;}
function getUserById(id: number, callback: FetchUserCallback): void { // Simulando llamada API asíncrona if (id < 0) { callback(new Error("Invalid ID")); // ID inválido } else { callback(null, { id, name: "User " + id }); // Usuario + id }}4. 🏗️ Tipos personalizados con Type
// Definir un tipotype Point = { x: number; y: number;};
// Usar el tipoconst point: Point = { x: 10, y: 20 };
// Union typestype ID = string | number;
let identifier: ID = 123;identifier = "ABC123"; // También es válido
// Intersection Typestype Coords = { lat: number; long: number;};
type Place = { name: string; address: string;};
type FullLocation = Coords & Place;
const myLocation: FullLocation = { lat: 40.7128, long: -74.0060, name: "Times Square", address: "Manhattan, NY 10036"};🔍 Diferencias entre Type e Interface
- Interface:
- Se puede extender con
extends. - Se puede declarar múltiples veces para añadir campos (declaration merging)
- Ideal para definir API públicas y con orientación a objetos.
- Se puede extender con
- Type:
- Puede usar operadores de unión (
|) e intersección (&) - No se puede redeclarar.
- Útil para alias de tipos y casos más complejos.
- Puede usar operadores de unión (
// Declaration merging con interfacesinterface Animal { name: string;}
interface Animal { age: number;}
const dog: Animal = { name: "Bobby", age: 5}; // Válido, los campos se combinan
// Los tipos no se pueden combinar como las interfacestype Person = { name: string;};
// Error: Identificador duplicado 'Person'// type Person = {// age: number;// };
// Tipos de unión con Type (no es posible con interfaces)type Status = "pending" | "approved" | "rejected";
// Comparando implementación
// Con Interface:interface Car { brand: string; model: string;}
// Con Type:type Vehicle = { brand: string; model: string;};
// Ambos se pueden extender, pero con sintaxis diferente
// Extendiendo interfaceinterface SportsCar extends Car { topSpeed: number;}
// Extendiendo tipo mediante interseccióntype SportVehicle = Vehicle & { topSpeed: number;};5. ⚡ Funciones en TypeScript
// Tipos para parámetros y valor de retornofunction sum(a: number, b: number): number { return a + b;}
// Parámetros opcionalesfunction createMessage(user: string, message?: string): string { return message ? `${user} dice: ${message}` : `${user} está conectado`;}
// Parámetros por defectofunction crearSaludo(nombre: string, saludo: string = "Hola"): string { return `${saludo}, ${nombre}!`;}
// Rest parametersfunction sumAllNumbers(...numbers: number[]): number { return numbers.reduce((total, n) => total + n, 0);}6. 📚 Recursos de aprendizaje adicionales
7. 🏋️♀️ Ejercicios prácticos
- Crear una interfaz
Productcon propiedades como nombre, precio, stock y una función para calcular el valor total. - Implementar un sistema de tipos para un carrito de compras con funciones para añadir/eliminar productos y calcular el total.
- Convertir una función JavaScript existente a TypeScript añadiendo tipos adecuados.