TypeScript
Introducción
Sección titulada «Introducción»🔷 ¿Qué es TypeScript?
Sección titulada «🔷 ¿Qué es TypeScript?»TypeScript es un superset de JavaScript desarrollado por Microsoft que añade tipado estático opcional. Todo código JavaScript válido es también TypeScript válido. Al compilar, TypeScript produce JavaScript puro que cualquier navegador puede ejecutar.
La principal limitación de JavaScript es que los errores solo se detectan en tiempo de ejecución, cuando el usuario ya está usando la aplicación. TypeScript los detecta mientras se escribe el código, antes de ejecutar nada.
📌 Variables
Sección titulada «📌 Variables»TypeScript permite declarar explícitamente el tipo de cada variable. Si no se
declara, el compilador infiere el tipo según el valor asignado. Las dos palabras
clave modernas son const (inmutable) y let (mutable). Evite var, tiene alcance
de función y genera comportamientos inesperados.
// Tipado explícitoconst nombre: string = "Ana";let edad: number = 21;const activo: boolean = true;
// TypeScript infiere el tipo del valor inicialconst pi = 3.14; // inferido: numberconst saludo = "Hola"; // inferido: string
// let puede reasignarse al mismo tipolet contador: number = 0;contador = 1; // ✓ OK// contador = "uno"; ✗ Error: string no es number🔀 Estructuras de Control
Sección titulada «🔀 Estructuras de Control»TypeScript usa las mismas estructuras de control que JavaScript, if/else, switch y
bucles, con la ventaja de que el compilador verifica que las condiciones sean del tipo
correcto.
// if / elseconst nota: number = 3.5;if (nota >= 3.0) { console.log("Aprobado");} else { console.log("Reprobado");}
// switchenum Dia { Lunes, Martes, Miercoles }const hoy: Dia = Dia.Lunes;switch (hoy) { case Dia.Lunes: console.log("Inicio de semana"); break; case Dia.Martes: console.log("Segundo día"); break; default: console.log("Otro día");}
// for...of, iterar un arreglo tipadoconst cursos: string[] = ["ISIS2603", "MISO4208"];for (const curso of cursos) { console.log(curso);}
// whilelet i: number = 0;while (i < 3) { console.log(i); i++;}enum Dia { Lunes, Martes, Miercoles }
const hoy: Dia = Dia.Lunes;
(hoy) {
case Dia.Lunes: console.log("Inicio de semana"); ;
default: console.log("Otro día");
}Tipos primitivos
Sección titulada «Tipos primitivos»🔢 number
Sección titulada «🔢 number»En TypeScript (y JavaScript) todos los números, enteros y decimales, son del tipo
number. No existe un tipo int separado. Internamente usa el estándar IEEE 754 de
doble precisión.
// Enteros y decimales: mismo tipoconst edad: number = 21;const precio: number = 19.99;const negativo: number = -5;
// Operadores aritméticosconst suma = edad + precio; // 40.99const resto = 10 % 3; // 1, móduloconst potencia = 2 ** 8; // 256, exponenciación
// Valores especialesconst infinito = 1 / 0; // Infinityconst noEsNum = 0 / 0; // NaNNumber.isNaN(noEsNum); // trueNumber.isFinite(infinito); // falseNumber.parseInt("42px"); // 42Number.parseFloat("3.14abc"); // 3.14
// Math, operaciones comunesMath.round(3.7); // 4, redondear al entero más cercanoMath.floor(3.9); // 3, redondear hacia abajoMath.ceil(3.1); // 4, redondear hacia arribaMath.abs(-7); // 7, valor absolutoMath.max(1,5,3); // 5, máximoMath.min(1,5,3); // 1, mínimoMath.sqrt(16); // 4, raíz cuadradaMath.pow(2, 10); // 1024🔤 string
Sección titulada «🔤 string»Los strings representan texto. TypeScript soporta tres formas de delimitarlos:
comillas simples, dobles y acentos graves (template literals). Las tres crean el
mismo tipo string; la diferencia está en sus capacidades.
// ① Comillas simplesconst a: string = 'Hola mundo';
// ② Comillas doblesconst b: string = "Hola mundo";
// ③ Template literals (acento grave / backtick `)// · Interpolación con ${expresión}// · Pueden ocupar varias líneas sin \nconst nombre = "Ana";const c: string = `Hola, ${nombre}!`; // "Hola, Ana!"const multi = `Primera líneaSegunda línea`;
// Métodos de manipulaciónconst s = " TypeScript ";s.trim(); // "TypeScript"s.toUpperCase(); // " TYPESCRIPT "s.toLowerCase(); // " typescript "
const t = "ISIS2603";t.length; // 8t.includes("2603"); // truet.startsWith("ISIS"); // truet.endsWith("3"); // truet.indexOf("26"); // 4t.slice(0, 4); // "ISIS"t.replace("ISIS", "MISO"); // "MISO2603"t.split(""); // ["I","S","I","S","2","6","0","3"]"ha".repeat(3); // "hahaha"const s = " TypeScript ";
s.(); // "TypeScript"
const t = "ISIS2603";
t.("2603"); // true
t.(); // "ISIS2603"☑️ boolean
Sección titulada «☑️ boolean»El tipo boolean solo tiene dos valores posibles: true y false. Es el tipo de
retorno de las comparaciones y el tipo esperado en condiciones de if y while.
const activo: boolean = true;const inactivo: boolean = false;
// Operadores de comparación, producen boolean5 === 5; // true, igualdad estricta (tipo + valor)5 !== 3; // true, desigualdad estricta5 > 3; // true5 < 3; // false"a" === "a"; // true
// Operadores lógicosconst mayorDeEdad = activo && edad >= 18; // AND, ambos trueconst puedeAcceder = activo || esAdmin; // OR, al menos uno trueconst noActivo = !activo; // NOT → false
// Truthy y falsy// Falsy: 0, "", null, undefined, NaN// Truthy: cualquier otro valorif (0) { /* nunca ejecuta */ }if ("") { /* nunca ejecuta */ }if (42) { /* sí ejecuta */ }if ("x") { /* sí ejecuta */ }5 5; // true, mismo tipo y valor "1" == 1; // true, == sí convierte tipos "1" 1; // false, string distinto de number
🔵 null, undefined y tipos especiales de TS
Sección titulada «🔵 null, undefined y tipos especiales de TS»TypeScript añade varios tipos para describir situaciones especiales: ausencia de valor, tipos incógnitos, funciones que nunca retornan, etc.
// undefined, variable declarada sin valor iniciallet sinValor: string | undefined;console.log(sinValor); // undefined
// null, ausencia intencional de valorlet usuario: string | null = null;usuario = "Ana"; // se asigna después
// any, desactiva el chequeo de tipos (evitar)let dato: any = "texto";dato = 42; // sin error, sin protección
// unknown, alternativa segura a any// hay que verificar el tipo antes de usarlet entrada: unknown = obtenerValor();if (typeof entrada === "string") { console.log(entrada.toUpperCase()); // seguro}
// void, función que no retorna valorfunction log(msg: string): void { console.log(msg);}
// never, función que NUNCA retorna (siempre lanza error)function fallo(msg: string): never { throw new Error(msg);}
// ??, nullish coalescing: valor por defecto si null/undefinedconst nombre = usuario ?? "Anónimo";
// ?., optional chaining: acceso seguro a propiedadesconst longitud = usuario?.length; // undefined si null
// as, type assertion: indicar el tipo exacto al compiladorconst btn = document.getElementById("btn") as HTMLButtonElement;let usuario: string | null = null; const nombre = usuario "Anónimo"; // "Anónimo" const longitud = usuariolength; // undefined, sin error
Datos complejos
Sección titulada «Datos complejos»📦 Tipos de Datos Complejos
Sección titulada «📦 Tipos de Datos Complejos»Además de los primitivos, TypeScript soporta estructuras de datos más ricas: enumeraciones para conjuntos de constantes nombradas, arreglos para colecciones homogéneas, y tuplas para colecciones de longitud y tipos fijos.
// Enum, conjunto de constantes nombradasenum Rol { Estudiante, Profesor, Admin }const miRol: Rol = Rol.Estudiante;
// Array, dos sintaxis equivalentesconst notas: number[] = [4.5, 3.8, 5.0];const nombres: Array<string> = ["Ana", "Luis"];
// Tuple, longitud y tipos estrictamente fijosconst coordenada: [number, number] = [4.71, -74.07];const entrada: [string, number] = ["Ana", 21];⚙️ Funciones
Sección titulada «⚙️ Funciones»En TypeScript las funciones declaran el tipo de cada parámetro y el tipo de retorno. Un
parámetro seguido de ? es opcional. Las funciones también son objetos, se pueden
pasar como argumentos o devolver desde otras funciones.
// Parámetros tipados y tipo de retorno explícitofunction sumar(a: number, b: number): number { return a + b;}
// Parámetro opcional con ?function saludar(nombre: string, titulo?: string): string { return `Hola, ${titulo ?? ''} ${nombre}`;}saludar("Ana"); // "Hola, Ana"saludar("Ana", "Dra."); // "Hola, Dra. Ana"
// Arrow functionconst doble = (x: number): number => x * 2;
// Función como argumento (las funciones son objetos)function aplicar(fn: (n: number) => number, val: number): number { return fn(val);}aplicar(doble, 5); // 10🔮 ¿Qué crees que imprime este código?
function saludar(nombre: string, titulo?: string): string {
return `Hola, ${titulo ?? ''} ${nombre}`;
}
console.log(saludar("Ana"));Salida real: Hola, Ana
titulo no se pasó, así que ?? usa el valor por defecto ''. El resultado queda 'Hola, ' + '' + ' ' + 'Ana', por eso aparecen dos espacios seguidos entre la coma y 'Ana'.
➡️ Operador flecha =>
Sección titulada «➡️ Operador flecha =>»El operador => define una función flecha (arrow function). Es una sintaxis
compacta que reemplaza la palabra clave function y tiene dos variantes según la
complejidad del cuerpo.
// Sintaxis completa: con llaves y return explícitoconst cuadrado = (x: number): number => { return x * x;};
// Retorno implícito: sin llaves, una sola expresiónconst cuadrado = (x: number): number => x * x;
// Un solo parámetro: los paréntesis son opcionalesconst doble = (x: number) => x * 2;
// Sin parámetros: paréntesis vacíos obligatoriosconst ahora = (): Date => new Date();La flecha brilla especialmente en los métodos de array map, filter y forEach,
donde la función se pasa como argumento:
const notas: number[] = [3.2, 4.5, 2.8, 4.9];
// map, transforma cada elementoconst redondeadas = notas.map(n => Math.round(n));// [3, 5, 3, 5]
// filter, conserva los que cumplen la condiciónconst aprobadas = notas.filter(n => n >= 3.0);// [3.2, 4.5, 4.9]
// forEach, ejecuta una acción por cada elementoaprobadas.forEach(n => console.log(`Nota: ${n}`));// Retorno implícito, sin llaves ni return
const doble = (x: number) => x * 2;
// Retorno explícito, con llaves
const cuadrado = (x: number): number => {
x * x;
};Pruébelo usted mismo: modifique el arreglo notas, cambie el umbral del filter, o
agregue una nueva operación al map, y ejecute el código para ver la salida real.
Consola:
// la salida aparece aquí al presionar Ejecutar🏛️ Clases e Interfaces
Sección titulada «🏛️ Clases e Interfaces»JavaScript, a partir de ECMAScript 2015, introdujo la palabra clave class al lenguaje. TypeScript se apoya
en esa sintaxis y le añade tipos: los atributos, los parámetros del constructor y los
métodos quedan tipados, y el compilador verifica que todo compile antes de ejecutar nada.
Aquí conviene separar dos ideas que se confunden fácilmente:
- Una clase es una plantilla que, al invocarla con
new, produce objetos concretos en memoria. La clase existe en el JavaScript compilado. - Una interface es un contrato: describe qué forma debe tener un objeto (o qué métodos debe tener una clase), pero no crea nada por sí misma. Es una construcción exclusiva de TypeScript que desaparece por completo al compilar a JavaScript.
🧱 Clases: plantillas para crear objetos
Sección titulada «🧱 Clases: plantillas para crear objetos»Declarar una clase no crea ningún objeto todavía, solo define la forma que tendrán sus
instancias. El objeto real aparece cuando se invoca el constructor con new.
class Course { name: string; credits: number; teacherName: string;
constructor(name: string, credits: number, teacherName: string) { this.name = name; this.credits = credits; this.teacherName = teacherName; }}
// Course es la plantilla; new Course(...) crea el objeto en memoriaconst isis2603 = new Course("Ingeniería de Software", 3, "Ana Ruiz");const miso4203 = new Course("Bases de Datos", 4, "Luis Pardo");
console.log(isis2603.name); // "Ingeniería de Software"console.log(isis2603.credits); // 3
// Cada new produce un objeto independiente: modificar uno no afecta al otroisis2603.credits = 4;console.log(miso4203.credits); // sigue siendo 4, no cambió
// Un arreglo de objetos creados con la misma claseconst cursos: Course[] = [isis2603, miso4203];cursos.forEach(c => console.log(`${c.name}: ${c.credits} créditos`));TypeScript permite abreviar el constructor cuando lo único que hace es declarar y
asignar atributos: agregando public antes de cada parámetro, el atributo se declara y
se asigna en una sola línea, sin repetir this.x = x.
class Course { constructor( public name: string, public credits: number, public teacherName: string ) {}}
const isis2603 = new Course("Ingeniería de Software", 3, "Ana Ruiz");console.log(isis2603.name); // "Ingeniería de Software"📜 Interfaces: contratos que un objeto debe cumplir
Sección titulada «📜 Interfaces: contratos que un objeto debe cumplir»La forma más simple de interface describe únicamente la forma de los datos: qué atributos debe tener un objeto y de qué tipo.
interface CourseInfo { name: string; credits: number;}
// Un objeto literal cumple la interface si tiene la forma correcta,// sin decir en ningún lado "yo soy un CourseInfo"const isis2603: CourseInfo = { name: "Ingeniería de Software", credits: 3 };
// Error: falta credits, TypeScript no compila// const incompleto: CourseInfo = { name: "Cálculo" };Una interface también puede exigir métodos, no solo atributos. Cuando una clase se
compromete a cumplir ese contrato, lo declara explícitamente con implements:
interface Evaluable { calcularPromedio(): number;}
class Student implements Evaluable { constructor(public name: string, private grades: number[]) {}
calcularPromedio(): number { const suma = this.grades.reduce((acc, n) => acc + n, 0); return suma / this.grades.length; }}
const ana = new Student("Ana", [4.5, 3.8, 5.0]);console.log(ana.calcularPromedio()); // 4.433...🔮 ¿Qué crees que imprime este código?
class Student implements Evaluable {
constructor(public name: string, private grades: number[]) {}
calcularPromedio(): number {
const suma = this.grades.reduce((acc, n) => acc + n, 0);
return suma / this.grades.length;
}
}
const ana = new Student("Ana", [4.5, 3.8, 5.0]);
console.log(ana.calcularPromedio());Salida real: 4.433333333333334
reduce suma las notas (4.5 + 3.8 + 5.0 = 13.3) y luego se divide entre grades.length (3). 13.3 / 3 no da un número exacto, por eso aparecen tantos decimales: así es como JavaScript representa internamente ese resultado.
🧰 Utility Types: transformar tipos existentes
Sección titulada «🧰 Utility Types: transformar tipos existentes»A veces necesita un tipo que es casi igual a otro, pero le falta o le sobra alguna propiedad. En vez de declarar una interface nueva y repetir todos los atributos, TypeScript trae utility types: tipos genéricos que transforman un tipo existente.
interface Artista { id: number; nombre: string; paisOrigen: string; albumes: string[];}
// Omit<T, K>: el mismo tipo, sin las propiedades listadastype NuevoArtista = Omit<Artista, 'id' | 'albumes'>;// equivale a: { nombre: string; paisOrigen: string }
const nuevo: NuevoArtista = { nombre: 'Shakira', paisOrigen: 'Colombia', // id y albumes no van: Omit los quitó del tipo};Omit<Artista, 'id' | 'albumes'> describe exactamente lo que envía un formulario de
creación: todavía no hay id (lo asigna el servidor) y todavía no hay albumes (ese
formulario no los pide). Es el mismo contrato Artista, menos las dos propiedades que
no aplican todavía.
Autoevaluación
Sección titulada «Autoevaluación»Ejercicios prácticos
Sección titulada «Ejercicios prácticos»Escriba el nombre del tipo o la palabra clave correcta en cada campo. Use el botón Verificar para comprobar sus respuestas.
Ejercicio 1, Declarar variables tipadas
Sección titulada «Ejercicio 1, Declarar variables tipadas»Complete las anotaciones de tipo para que cada variable corresponda correctamente al valor que se le asigna.
const nombre: = "Ana"; let edad: = 25; const activo: = true;
Ejercicio 2, Función con parámetro opcional
Sección titulada «Ejercicio 2, Función con parámetro opcional»Complete la función: el parámetro titulo debe ser opcional y la función debe retornar
un string.
function saludar(nombre: string, titulo: string): {
return `Hola, ${titulo ?? ''} ${nombre}`;
}Ejercicio 3, Creación de objetos con new
Sección titulada «Ejercicio 3, Creación de objetos con new»Complete el código para crear dos objetos Course distintos a partir de la misma clase
y acceder a uno de sus atributos.
class Course {
constructor(public name: string, public credits: number) {}
}
const isis2603 = Course("Ingeniería de Software", 3);
const miso4203 = Course("Bases de Datos", 4);
console.log(isis2603.); // "Ingeniería de Software"Ejercicio 4, Clase que implementa una interface
Sección titulada «Ejercicio 4, Clase que implementa una interface»Complete el nombre de la interface, la palabra clave que enlaza la clase con ella, y el tipo de retorno del método.
interface {
describir(): string;
}
class Curso Describible {
constructor(public nombre: string) {}
describir(): {
return `Curso: ${this.nombre}`;
}
}Quiz conceptual
Sección titulada «Quiz conceptual»Ejercicios de análisis, funciones como parámetros
Sección titulada «Ejercicios de análisis, funciones como parámetros»Estos ejemplos profundizan en el patrón que ya vio en map, filter y forEach: una
función que recibe otra función como parámetro. No tienen una respuesta que el sitio
pueda verificar automáticamente, resuélvalos por su cuenta o discútalos con un compañero.
Ejemplo 1, un callback simple
Sección titulada «Ejemplo 1, un callback simple»function saludar(nombre: string, callback: (mensaje: string) => void): void { const mensaje = `Hola, ${nombre}!`; callback(mensaje);}
saludar("Ana", (msg) => { console.log(msg);});Ejercicio (explicar):
- ¿Cuál de los dos parámetros de
saludares en sí mismo una función? ¿Cómo se nota eso en su tipo? - Explique en sus palabras, paso a paso, qué pasa desde que se llama
saludar("Ana", ...)hasta que aparece el mensaje en consola. - ¿Qué pasaría si dentro de
saludarse borra la líneacallback(mensaje);? ¿Se produciría un error, o simplemente no pasaría nada? ¿Por qué?
Ejemplo 2, reimplementando map a mano
Sección titulada «Ejemplo 2, reimplementando map a mano»function aplicarATodos(numeros: number[], operacion: (n: number) => number): number[] { const resultado: number[] = []; for (const n of numeros) { resultado.push(operacion(n)); } return resultado;}
const numeros = [1, 2, 3, 4];const duplicados = aplicarATodos(numeros, (n) => n * 2);console.log(duplicados);Ejercicio (explicar):
- Sin ejecutar el código, diga qué imprime
console.log(duplicados). operaciones un parámetro deaplicarATodos, pero también es una función. Explique qué “sabe”aplicarATodossobre lo que haceoperacionpor dentro (¿nada, algo, todo?).- Modifique la llamada para que en vez de duplicar, le sume 10 a cada número. ¿Qué línea cambió y por qué esa y no otra?
- Este patrón es exactamente lo que hace
mapinternamente. ¿Por qué cree que anumeros.map(n => n * 2)no hay que decirle “a qué arreglo recorrer” mientras que aaplicarATodossí?
Ejemplo 3, dos funciones como parámetros (condición + acción)
Sección titulada «Ejemplo 3, dos funciones como parámetros (condición + acción)»function procesarSiCumple( numeros: number[], condicion: (n: number) => boolean, accion: (n: number) => void): void { for (const n of numeros) { if (condicion(n)) { accion(n); } }}
procesarSiCumple( [10, 15, 22, 7, 30], (n) => n % 2 === 0, (n) => console.log(`${n} es par`));Ejercicio (explicar):
- Identifique cuál función juega el papel de
filtery cuál el deforEachdentro deprocesarSiCumple. - Recorra el arreglo
[10, 15, 22, 7, 30]a mano, elemento por elemento, y diga cuáles pasan lacondiciony cuáles no. - ¿Qué imprime el programa completo?
- Reescriba la misma llamada usando
numeros.filter(...).forEach(...)en cadena. ¿Cambia el resultado?
Ejemplo 4, nombrando el tipo del callback
Sección titulada «Ejemplo 4, nombrando el tipo del callback»type Validador = (valor: string) => boolean;
function validarEntrada(valor: string, validador: Validador): void { if (validador(valor)) { console.log(`"${valor}" es válido`); } else { console.log(`"${valor}" no es válido`); }}
const noVacio: Validador = (v) => v.trim().length > 0;
validarEntrada("Hola", noVacio);validarEntrada(" ", noVacio);Ejercicio (explicar):
Validadorno es una clase ni una interfaz, ¿qué es exactamente? ¿Qué describe: un valor, o la forma de una función?- ¿Qué imprime cada llamada a
validarEntrada? Justifique con el comportamiento dev.trim().length > 0. - Escriba un segundo validador,
soloNumeros: Validador, que retornetruesolo si el string contiene únicamente dígitos, y pruébelo con"123"y"12a". - ¿Por qué declarar el tipo
Validadoraparte es mejor que escribir(valor: string, validador: (v: string) => boolean)directamente en la firma devalidarEntrada?