Programação Funcional em TypeScript: do A ao Z
Programação Funcional (PF) é um paradigma que trata a computação como a avaliação de funções matemáticas e evita a mudança de estado e dados mutáveis. TypeScript, sendo uma extensão estritamente tipada do JavaScript, fornece uma excelente base para aplicar os princípios da PF, permitindo escrever código mais previsível, testável e confiável. Neste artigo, vamos explorar os conceitos-chave da PF em TypeScript, começando pelos fundamentos e indo até técnicas avançadas.
Princípios fundamentais da programação funcional
Antes de nos aprofundarmos no código, é importante entender os princípios fundamentais sobre os quais a PF é construída.
Funções Puras (Pure Functions)
Uma função pura é uma função que:
- Sempre retorna o mesmo resultado para os mesmos argumentos (determinismo).
- Não tem efeitos colaterais (não altera variáveis externas, não exibe dados no console, não envia requisições).
Exemplo de função pura:
function add(a: number, b: number): number { return a + b;}
// Sempre previsívelconsole.log(add(2, 3)); // 5console.log(add(2, 3)); // 5Exemplo de função impura:
let counter = 0;function increment(): number { return ++counter; // Efeito colateral: alteração de variável externa}Imutabilidade (Immutability)
Os dados não devem ser alterados após a criação. Em vez de modificar um objeto existente, criamos uma nova cópia com as alterações necessárias.
interface User { name: string; age: number;}
const user: User = { name: "Alice", age: 30 };
// Ruim (mutação)user.age = 31;
// Bom (imutabilidade)const updatedUser: User = { ...user, age: 31 };
console.log(user); // { name: "Alice", age: 30 } - original não mudouconsole.log(updatedUser); // { name: "Alice", age: 31 }Composição e Currying em TypeScript
Composição de funções é o processo de combinar várias funções simples para criar uma mais complexa. Currying é a transformação de uma função com vários argumentos em uma sequência de funções, cada uma recebendo um único argumento.
Composição (Function Composition)
Implementação tipada de composição em TypeScript:
function compose<A, B, C>(f: (x: B) => C, g: (x: A) => B): (x: A) => C { return (x: A) => f(g(x));}
const toUpperCase = (str: string): string => str.toUpperCase();const exclaim = (str: string): string => `${str}!`;
const shout = compose(exclaim, toUpperCase);console.log(shout("hello world")); // "HELLO WORLD!"Currying
Exemplo de função curried:
function multiply(a: number): (b: number) => number { return (b: number) => a * b;}
const double = multiply(2);const triple = multiply(3);
console.log(double(5)); // 10console.log(triple(5)); // 15Currying torna as funções mais flexíveis e convenientes para composição.
Trabalhando com mônadas: Option e Result
Mônadas são um padrão poderoso para lidar com efeitos colaterais e tratamento de erros sem quebrar a pureza das funções. Em TypeScript, as mônadas Option (ou Maybe) e Result (ou Either) são frequentemente usadas.
Mônada Option (Maybe)
Permite trabalhar com segurança com valores que podem ser null ou undefined.
type Option<T> = T | null;
function safeDivide(a: number, b: number): Option<number> { return b === 0 ? null : a / b;}
function getValue(value: Option<number>): string { if (value === null) { return "Erro: divisão por zero"; } return `Resultado: ${value}`;}
console.log(getValue(safeDivide(10, 2)