Programación funcional en TypeScript: nivel avanzado

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Programación funcional en TypeScript: nivel avanzado

\n\n

La programación funcional (PF) es un paradigma que considera los cálculos como la evaluación de funciones matemáticas y evita el cambio de estado y los datos mutables. TypeScript, al ser una extensión de JavaScript, proporciona un potente sistema de tipos que permite implementar muchos conceptos de PF con un nuevo nivel de seguridad y expresividad. En este artículo, exploraremos técnicas avanzadas: desde la inmutabilidad estricta hasta el trabajo con mónadas y functores.

\n\n

Inmutabilidad y trabajo con estructuras inmutables

\n\n

La inmutabilidad es una piedra angular de la PF. En TypeScript, podemos garantizarla a nivel de tipos usando los modificadores readonly y tipos utilitarios.

\n\n
// Inmutabilidad profunda con ReadonlyDeep\ntype DeepReadonly<T> = {\n  readonly [P in keyof T]: DeepReadonly<T[P]>;\n};\n\ninterface User {\n  name: string;\n  address: { city: string; zip: number };\n}\n\nconst user: DeepReadonly<User> = {\n  name: "Alice",\n  address: { city: "Moscow", zip: 101000 }\n};\n\n// user.address.city = "SPb"; // ¡Error! Propiedad de solo lectura
\n\n

Para actualizar datos de forma segura, use funciones puras que devuelvan nuevos objetos:

\n\n
function updateZip(user: DeepReadonly<User>, newZip: number): DeepReadonly<User> {\n  return { ...user, address: { ...user.address, zip: newZip } };\n}\n\nconst updatedUser = updateZip(user, 190000);
\n\n

Composición de funciones y currificación

\n\n

La composición permite construir transformaciones complejas a partir de funciones simples. En TypeScript, con genéricos, podemos crear combinadores tiposeguros.

\n\n
// Tipo para la composición de dos funciones\ntype Compose<A, B, C> = (f: (b: B) => C, g: (a: A) => B) => (a: A) => C;\n\nconst compose: Compose<any, any, any> = (f, g) => (x) => f(g(x));\n\n// Ejemplo: convertir cadena a número e incrementar en 1\nconst toNumber = (s: string): number => parseInt(s, 10);\nconst increment = (n: number): number => n + 1;\n\nconst parseAndIncrement = compose(increment, toNumber);\nconsole.log(parseAndIncrement("5")); // 6
\n\n

Currificación convierte una función de varios argumentos en una secuencia de funciones de un solo argumento. Esto hace que la composición sea más flexible:

\n\n
// Función de currificación universal con tipos\nfunction curry<T1, T2, R>(fn: (a: T1, b: T2) => R): (a: T1) => (b: T2) => R {\n  return (a: T1) => (b: T2) => fn(a, b);\n}\n\nconst add = (a: number, b: number): number => a + b;\nconst curriedAdd = curry(add);\nconst add5 = curriedAdd(5);\nconsole.log(add5(3)); // 8
\n\n

Functores y mónadas: trabajo con contextos

\n\n

Un functor es una estructura de datos que soporta la operación map. En TypeScript, un functor puede ser un array, una Promise o un tipo personalizado.

\n\n
// Functor Option (Maybe) para trabajo seguro con null/undefined\ninterface Option<T> {\n  map<U>(fn: (value: T) => U): Option<U>;\n  getOrElse(defaultValue: T): T;\n}\n\nclass Some<T> implements Option<T> {\n  constructor(private value: T) {}\n  map<U>(fn: (value: T) => U): Option<U> {\n    return new Some(fn(this.value));\n  }\n  getOrElse(_: T): T {\n    return this.value;\n  }\n}\n\nclass None<T> implements Option<T> {\n  map<U>(_: (value: T) => U): Option<U> {\n    return new None<U>();\n  }\n  getOrElse(defaultValue: T): T {\n    return defaultValue;\n  }\n}\n\n// Uso\nconst maybeNumber: Option<number> = new Some(42);\nconst result = maybeNumber.map(x => x * 2).getOrElse(0); // 84
\n\n

Una mónada extiende el functor con el método flatMap (o bind

Blogs

Book Recommendations