Programação Funcional em TypeScript: Nível Avançado

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Programação Funcional em TypeScript: nível avançado

Programação Funcional (PF) é um paradigma que, nos últimos anos, vem conquistando ativamente o mundo do JavaScript e TypeScript. Para desenvolvedores já familiarizados com os fundamentos (funções puras, imutabilidade de dados), abre-se uma nova camada de possibilidades: trabalhar com mônadas, functores, aplicativos e composições complexas. Neste artigo, vamos analisar conceitos avançados de PF em TypeScript com exemplos práticos.



1. Imutabilidade e compartilhamento estrutural de dados

Imutabilidade é a pedra angular da PF. Em TypeScript, podemos garantir a imutabilidade no nível de tipos usando readonly e ReadonlyArray. No entanto, para trabalhar eficientemente com grandes estruturas de dados (por exemplo, Redux store), utiliza-se o compartilhamento estrutural (structural sharing) — bibliotecas como Immer ou Immutable.js.

Vamos considerar um exemplo usando readonly e uma função para atualizar um objeto aninhado sem mutação:

type User = {  readonly id: number;  readonly name: string;  readonly address: {    readonly city: string;    readonly zip: string;  };};

const updateUserCity = (user: User, newCity: string): User => ({ ...user, address: { ...user.address, city: newCity, },});

const user: User = { id: 1, name: 'Alice', address: { city: 'NYC', zip: '10001' } };const updatedUser = updateUserCity(user, 'LA');console.log(user.address.city); // NYC (original não foi alterado)console.log(updatedUser.address.city); // LA

Observe: criamos um novo objeto, mas reutilizamos as partes não modificadas (por exemplo, id e name). Isso é o compartilhamento estrutural manual. Para aninhamento profundo, é melhor usar bibliotecas.



2. Functores e sua implementação

Functor é um contêiner que implementa o método map, permitindo aplicar uma função ao conteúdo do contêiner sem violar sua estrutura. Em TypeScript, um functor típico é Array. Mas podemos criar nosso próprio functor, por exemplo, Maybe, para evitar erros com null e undefined.

Vamos implementar um functor Maybe simples:

class Maybe<T> {  private constructor(private value: T | null) {}

static of<U>(val: U | null | undefined): Maybe<U> { return new Maybe(val ?? null); }

map<U>(fn: (val: T) => U): Maybe<U> { if (this.value === null) { return Maybe.of<U>(null); } return Maybe.of(fn(this.value)); }

getOrElse(defaultValue: T): T { return this.value !== null ? this.value : defaultValue; }}

// Usoconst result = Maybe.of('hello') .map(s => s.toUpperCase()) .map(s => s + ' world') .getOrElse('fallback');console.log(result); // "HELLO WORLD"

const empty = Maybe.of<string>(null) .map(s => s.toUpperCase()) .getOrElse('default');console.log(empty); // "default"

O functor Maybe permite encadear chamadas de funções com segurança, sem se preocupar com null. Cada map retorna um novo Maybe, preservando o contexto.



3. Mônadas: cadeias de operações com efeitos colaterais

Mônada é um functor que também implementa o método flatMap (frequentemente chamado de bind ou chain). Ele permite "desembrulhar" contêineres aninhados. Um exemplo clássico de mônada é Promise ou Array. Vamos considerar a mônada Either para tratamento de erros sem exceções.

type Either<L, R> = Left<L> | Right<R>;

class Left<L> { readonly value: L; constructor(value: L) { this.value = value; } isLeft(): this is Left<L> { return true; } isRight(): this is Right<ne

Blogs

Book Recommendations