Programación funcional en TypeScript: de la teoría a la práctica
La programación funcional (PF) es un paradigma basado en el cálculo mediante la aplicación de funciones y la evitación de cambios de estado. TypeScript, al ser un superconjunto estrictamente tipado de JavaScript, proporciona herramientas potentes para implementar PF. En este artículo analizaremos los conceptos clave de la programación funcional en TypeScript con ejemplos prácticos de código.
1. Funciones puras e inmutabilidad
Función pura es una función que:
- Siempre devuelve el mismo resultado para los mismos argumentos (determinismo).
- No tiene efectos secundarios (no modifica variables externas, no realiza operaciones de I/O).
// Función puraconst add = (a: number, b: number): number => a + b;
// Función impura (efecto secundario — modificación del estado externo)let counter = 0;const increment = (): number => ++counter;Inmutabilidad significa que los datos no se modifican, sino que se crean nuevas copias. En TypeScript se puede usar el operador spread y métodos sin mutaciones:
interface User { name: string; age: number;}
// Mutación (malo)const updateAgeMutable = (user: User, newAge: number): void => { user.age = newAge;};
// Enfoque inmutable (bueno)const updateAgeImmutable = (user: User, newAge: number): User => ({ ...user, age: newAge,});La inmutabilidad simplifica la depuración y las pruebas, ya que los datos siempre son predecibles.
2. Funciones de orden superior y clausuras
Funciones de orden superior son funciones que reciben otras funciones como argumentos o las devuelven. Ejemplo — el método Array.map:
const numbers: number[] = [1, 2, 3, 4];const doubled: number[] = numbers.map((x) => x * 2);console.log(doubled); // [2, 4, 6, 8]Clausura es una función que "recuerda" las variables del ámbito externo incluso después de que la función externa haya terminado su ejecución:
const createMultiplier = (factor: number): ((x: number) => number) => { return (x: number) => x * factor;};
const double = createMultiplier(2);const triple = createMultiplier(3);
console.log(double(5)); // 10console.log(triple(5)); // 15Las clausuras se utilizan ampliamente para crear funciones con aplicación parcial de argumentos (currying).
3. Composición de funciones y currying
Composición de funciones es la combinación de varias funciones en una, donde la salida de una función es la entrada de la siguiente. En TypeScript se puede escribir una utilidad para la composición:
const compose = <T, R>(...fns: Array<(arg: T) => T>) => (arg: T): T => fns.reduceRight((acc, fn) => fn(acc), arg);
// Ejemplo: transformación de cadenaconst toUpperCase = (s: string): string => s.toUpperCase();const exclaim = (s: string): string => `${s}!`;const shout = compose(toUpperCase, exclaim);
console.log(shout("hello")); // "HELLO!"Currying es la transformación de una función con varios argumentos en una secuencia de funciones con un solo argumento:
// Función normalconst add = (a: number, b: number): number => a + b;
// Versión currificadaconst curriedAdd = (a: number) => (b: number): number => a + b;
const add5 = curriedAdd(5);console.log(add5(3)); // 8El currying simplifica la creación de funciones especializadas y mejora la reutilización del código.
4. Mónadas y trabajo con Option/Maybe
Una mónada es un patrón de programación funcional que encapsula un valor y proporciona una interfaz para transformarlo. Una de las mónadas más simples es Maybe (u Option), que maneja de forma segura n