Saltar al contenido

Clase 17 - Introducción TypeScript

#ts #clase

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):

Ventana de terminal
mkdir mi-proyecto-ts
cd mi-proyecto-ts
pnpm init
pnpm add -D typescript
tsc --init

El 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 tipos
let 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";
// Arrays
let 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 cadena
anyData = false;
// unknown - similar a any pero más seguro
let unknownValue: unknown = 10;
unknownValue = "hello"; // hola
unknownValue = true;
// Necesitas verificar el tipo antes de operaciones específicas
if (typeof unknownValue === "string") {
console.log(unknownValue.toUpperCase());
}
// Diferencia entre any y unknown
let 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 any siempre que sea posible, ya que elimina los beneficios del sistema de tipos de TypeScript. Usa unknown como 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 tipo
let 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 DOM
const 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 interfaz
interface User {
name: string;
age: number;
email?: string; // Propiedad opcional con ?
readonly id: number; // Propiedad de solo lectura
}
// Uso de la interfaz
const user1: User = {
name: "Carlos",
age: 28,
id: 1
};
// user1.id = 2; // Error: no se puede modificar una propiedad readonly
// Extendiendo interfaces
interface 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 funciones
interface 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ón
interface 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)); // 8
console.log(subtract(5, 3)); // 2
// Interfaz con callback
interface 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 tipo
type Point = {
x: number;
y: number;
};
// Usar el tipo
const point: Point = { x: 10, y: 20 };
// Union types
type ID = string | number;
let identifier: ID = 123;
identifier = "ABC123"; // También es válido
// Intersection Types
type 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.
  • Type:
    • Puede usar operadores de unión (|) e intersección (&)
    • No se puede redeclarar.
    • Útil para alias de tipos y casos más complejos.
// Declaration merging con interfaces
interface 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 interfaces
type 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 interface
interface SportsCar extends Car {
topSpeed: number;
}
// Extendiendo tipo mediante intersección
type SportVehicle = Vehicle & {
topSpeed: number;
};

5. ⚡ Funciones en TypeScript

// Tipos para parámetros y valor de retorno
function sum(a: number, b: number): number {
return a + b;
}
// Parámetros opcionales
function createMessage(user: string, message?: string): string {
return message ? `${user} dice: ${message}` : `${user} está conectado`;
}
// Parámetros por defecto
function crearSaludo(nombre: string, saludo: string = "Hola"): string {
return `${saludo}, ${nombre}!`;
}
// Rest parameters
function sumAllNumbers(...numbers: number[]): number {
return numbers.reduce((total, n) => total + n, 0);
}

6. 📚 Recursos de aprendizaje adicionales

7. 🏋️‍♀️ Ejercicios prácticos

  1. Crear una interfaz Product con propiedades como nombre, precio, stock y una función para calcular el valor total.
  2. Implementar un sistema de tipos para un carrito de compras con funciones para añadir/eliminar productos y calcular el total.
  3. Convertir una función JavaScript existente a TypeScript añadiendo tipos adecuados.