
Escuchar este post
Selecciona una voz y genera audio para escuchar este post
Una guía rápida para principiantes que quieren dominar el manejo de efectos en TypeScript ✅
¿Qué es Effect y por qué debería importarte?
Si has trabajado con TypeScript durante un tiempo, probablemente te has enfrentado a algunos desafíos comunes: manejar errores de manera elegante, trabajar con operaciones asíncronas complejas, o simplemente escribir código que sea tanto robusto como fácil de mantener. Effect es una biblioteca revolucionaria que transforma la manera en que abordamos estos problemas. 🤓
Effect no es solo otra biblioteca de utilidades. Es un sistema completo para manejar efectos secundarios de manera funcional, proporcionando herramientas poderosas para crear aplicaciones más confiables y predecibles.
¿Qué son los "Efectos"?
Antes de sumergirnos en Effect, entendamos qué son los efectos en programación:
// ❌ Función pura - sin efectos function sumar(a: number, b: number): number { return a + b; } // ⚠️ Función con efectos - puede fallar, es asíncrona async function obtenerUsuario(id: string): Promise<Usuario> { const response = await fetch(`/api/users/${id}`); return response.json(); }
Los efectos son operaciones que pueden:
- Fallar (errores)
- Ser asíncronas (promesas)
- Depender del contexto (configuración, base de datos)
- Tener efectos secundarios (logs, mutaciones)
Tu Primera Experiencia con Effect
Instalación
npm install effect
El Tipo Effect
El corazón de Effect es el tipo Effect<Success, Error, Requirements>
:
import { Effect } from "effect"; // Effect que puede: // - Retornar un string (Success) // - Fallar con Error (Error) // - No necesita dependencias (never) const saludo: Effect.Effect<string, Error, never> = Effect.succeed("¡Hola, Effect!");
Ejemplo Práctico: API de Usuario
Veamos cómo Effect transforma el código típico de manejo de APIs:
Antes (JavaScript/TypeScript tradicional):
// ❌ Código tradicional - propenso a errores async function obtenerPerfilUsuario(id: string) { try { const response = await fetch(`/api/users/${id}`); if (!response.ok) { throw new Error(`HTTP ${response.status}`); } const usuario = await response.json(); if (!usuario.email) { throw new Error("Usuario sin email"); } return { nombre: usuario.name, email: usuario.email, activo: usuario.active ?? false }; } catch (error) { console.error("Error obteniendo usuario:", error); throw error; } }
Después (con Effect):
import { Effect, pipe } from "effect"; // ✅ Código con Effect - robusto y expresivo const obtenerPerfilUsuario = (id: string) => pipe( Effect.tryPromise({ try: () => fetch(`/api/users/${id}`), catch: (error) => new FetchError(error) }), Effect.flatMap(response => response.ok ? Effect.tryPromise({ try: () => response.json(), catch: (error) => new ParseError(error) }) : Effect.fail(new HttpError(response.status)) ), Effect.flatMap(usuario => usuario.email ? Effect.succeed({ nombre: usuario.name, email: usuario.email, activo: usuario.active ?? false }) : Effect.fail(new ValidationError("Usuario sin email")) ) );
Conceptos Fundamentales
1. Creación de Effects
// Éxito inmediato const exito = Effect.succeed(42); // Fallo inmediato const fallo = Effect.fail(new Error("Algo salió mal")); // Effect que puede fallar const division = (a: number, b: number) => b === 0 ? Effect.fail(new Error("División por cero")) : Effect.succeed(a / b);
2. Composición con pipe
import { pipe } from "effect"; const procesarNumero = (n: number) => pipe( Effect.succeed(n), Effect.map(x => x * 2), // Transformar el valor Effect.flatMap(x => // Encadenar otro Effect x > 10 ? Effect.succeed(`Grande: ${x}`) : Effect.fail(new Error("Muy pequeño")) ) );
3. Manejo de Errores Elegante
const operacionSegura = pipe( operacionPeligrosa(), Effect.catchAll(error => { console.log("Error capturado:", error); return Effect.succeed("Valor por defecto"); }) );
Ventajas de Effect sobre Promesas
| Aspecto | Promesas | Effect |
| --- | --- | --- |
| Tipado de errores | ❌ any
en catch | ✅ Errores tipados |
| Composición | ⚠️ async/await | ✅ Pipe operator |
| Lazy evaluation | ❌ Se ejecutan inmediatamente | ✅ Solo cuando se ejecutan |
| Cancelación | ⚠️ Limitada | ✅ Cancelación completa |
| Dependencias | ❌ No hay sistema | ✅ Sistema de dependencias |
Ejemplo del Mundo Real: Sistema de Notificaciones
import { Effect, pipe } from "effect"; // Definir errores específicos class EmailError extends Error { readonly _tag = "EmailError"; } class SMSError extends Error { readonly _tag = "SMSError"; } // Servicios const enviarEmail = (email: string, mensaje: string) => Effect.tryPromise({ try: () => emailService.send(email, mensaje), catch: () => new EmailError("Fallo al enviar email") }); const enviarSMS = (telefono: string, mensaje: string) => Effect.tryPromise({ try: () => smsService.send(telefono, mensaje), catch: () => new SMSError("Fallo al enviar SMS") }); // Lógica de negocio const notificarUsuario = (usuario: Usuario, mensaje: string) => pipe( // Intentar email primero enviarEmail(usuario.email, mensaje), Effect.catchTag("EmailError", () => // Si falla, intentar SMS enviarSMS(usuario.telefono, mensaje) ), Effect.catchTag("SMSError", () => // Si ambos fallan, log y continuar Effect.sync(() => { console.log(`No se pudo notificar a ${usuario.nombre}`); return "Notificación fallida"; }) ) );
Ejecutando Effects
Los Effects son lazy - no hacen nada hasta que los ejecutas:
import { Effect } from "effect"; // Crear el Effect (no se ejecuta) const miEffect = Effect.succeed("¡Hola!"); // Ejecutarlo Effect.runPromise(miEffect).then(console.log); // "¡Hola!" // O con manejo de errores Effect.runPromiseExit(miEffect).then(result => { if (result._tag === "Success") { console.log("Éxito:", result.value); } else { console.log("Error:", result.cause); } });
Próximos Pasos
Effect es un ecosistema rico con muchas características avanzadas:
- Schema: Validación y transformación de datos
- Stream: Procesamiento de flujos de datos
- Fiber: Concurrencia estructurada
- Layer: Sistema de dependencias
- STM: Memoria transaccional
Enlaces relacionados
Effect representa un cambio de paradigma en cómo escribimos código TypeScript. Al adoptar principios de programación funcional y proporcionar herramientas poderosas para el manejo de efectos, Effect nos permite crear aplicaciones más robustas, mantenibles y predecibles. 🛩️
No necesitas reescribir toda tu aplicación de una vez. Puedes empezar adoptando Effect gradualmente en las partes más críticas de tu código, especialmente donde manejas operaciones asíncronas complejas
o necesitas un mejor control de errores.
¿Listo para comenzar tu viaje con Effect? El futuro de la programación funcional en TypeScript te espera. 🧙🏼♂️
¿Te gustó este artículo? Compártelo con tu equipo y ayuda a más desarrolladores a descubrir el poder de Effect.
Abrazo. Bliss. 🤓
Construye tu propio useOutsideClick
Checa este otro Post
