Closure em TypeScript

Closure em TypeScript

Neste artigo, vamos explicar closures em TypeScript.

YouTube Video

Closure em TypeScript

O que é uma Closure?

Closure refere-se à capacidade de manter referências ao escopo (ambiente) em que uma função foi definida, mesmo que a função seja chamada fora desse escopo. A seguir, closures serão explicados incluindo anotações de tipo.

Simplificando, uma closure combina uma função e o ambiente das variáveis onde a função foi definida, permitindo o acesso a esse ambiente quando a função é chamada.

Mecanismo Básico de Closures

Em TypeScript, quando uma função é definida dentro de outra função, fica evidente que a função interna pode acessar as variáveis da função externa. Aqui está um exemplo básico de closure com anotações de tipo.

 1function outerFunction(): () => void {
 2    let outerVariable: string = "I am from outer function";
 3
 4    function innerFunction(): void {
 5        // The inner function accesses the variable of the outer function
 6        console.log(outerVariable);
 7    }
 8
 9    return innerFunction;
10}
11
12const closure: () => void = outerFunction();
13closure();  // "I am from outer function"
  • O tipo de retorno de outerFunction é () => void, indicando que retorna uma função.
  • O tipo de innerFunction é explicitamente definido como void, indicando que não possui valor de retorno.

Usos e Vantagens das Closures

Encapsulamento de Dados

Abaixo está um exemplo de closure com anotações de tipo para encapsulamento de dados.

 1function createCounter(): () => number {
 2    let count: number = 0;
 3
 4    return function (): number {
 5        count += 1;
 6        return count;
 7    };
 8}
 9
10const counter: () => number = createCounter();
11console.log(counter());  // 1
12console.log(counter());  // 2
13console.log(counter());  // 3
  • A função createCounter retorna uma função do tipo () => number.
  • A variável count é definida como tipo number e é manipulada dentro da closure.

Funções de Alta Ordem

Closures são úteis ao criar funções de alta ordem. Abaixo está um exemplo de uma função de alta ordem com anotações de tipo claras.

 1function createMultiplier(multiplier: number): (value: number) => number {
 2    return function (value: number): number {
 3        return value * multiplier;
 4    };
 5}
 6
 7const double: (value: number) => number = createMultiplier(2);
 8console.log(double(5));  // 10
 9
10const triple: (value: number) => number = createMultiplier(3);
11console.log(triple(5));  // 15
  • createMultiplier recebe um argumento do tipo number e retorna uma função do tipo (value: number) => number.
  • A função interna também aceita value como tipo number e retorna o resultado como tipo number.

Exemplo de Implementação de Closures em TypeScript

Implemente um contador com limite de intervalo como uma closure com anotações de tipo adicionais.

 1function rangeCounter(min: number, max: number): () => number | string {
 2    let count: number = min;
 3
 4    return function (): number | string {
 5        if (count <= max) {
 6            return count++;
 7        } else {
 8            return `Count has exceeded the maximum value: ${max}`;
 9        }
10    };
11}
12
13const counter: () => number | string = rangeCounter(1, 5);
14
15console.log(counter());  // 1
16console.log(counter());  // 2
17console.log(counter());  // 3
18console.log(counter());  // 4
19console.log(counter());  // 5
20console.log(counter());  // "Count has exceeded the maximum value: 5"
  • A função rangeCounter retorna uma função que retorna um number ou uma string.
  • Na função interna, se count exceder max, retorna uma mensagem do tipo string; caso contrário, retorna um valor do tipo number.

Precauções ao Usar Closures

Possível Vazamento de Memória por Closures

Closures podem manter variáveis de um escopo externo, o que às vezes pode causar vazamento de memória. Closures desnecessárias precisam ser explicitamente liberadas da memória.

 1function createLeak(): () => void {
 2    // Large array consuming significant memory
 3    const largeArray: string[] = new Array(1000000).fill("leak");
 4
 5    // Closure capturing `largeArray`
 6    return function (): void {
 7        console.log(largeArray[0]); // Using the captured array
 8    };
 9}
10
11// Create a closure that holds a reference to the large array
12let leakyFunction = createLeak();
13
14// The large array is not released as `leakyFunction` still references it
15
16// When the object is no longer needed
17leakyFunction = null; // Explicitly remove the reference
  • Neste código, o largeArray criado dentro de createLeak deveria ser liberado quando sai do escopo, mas não é porque a closure captura largeArray. Enquanto leakyFunction existir, essa memória desnecessária continuará sendo retida.
  • Quando um objeto ou variável não é mais necessário, definir sua referência como null permite que o coletor de lixo a detecte e libere a memória.

Mau Uso de Closures em Laços

Ao criar closures dentro de um laço, podem ocorrer problemas ao referenciar a mesma variável. O exemplo a seguir demonstra um caso onde a variável i não funciona corretamente.

1for (var i: number = 0; i < 3; i++) {
2    setTimeout((): void => {
3        console.log(i);
4    }, 1000);
5}
6// Output: 3, 3, 3

Este código não produz o resultado desejado porque i refere-se ao valor 3 no final do laço. Para corrigir isso, use let para separar o escopo ou utilize uma função auto-invocada.

1for (let i: number = 0; i < 3; i++) {
2    setTimeout((): void => {
3        console.log(i);
4    }, 1000);
5}
6// Output: 0, 1, 2

Ao usar let, o escopo de i é separado para cada iteração do laço, gerando os resultados esperados.

Resumo

Em TypeScript, closures podem resultar em código mais seguro e previsível aproveitando o sistema de tipos. O uso adequado de closures permite o encapsulamento de dados e um design flexível de funções de alta ordem. Além disso, é preciso ter cuidado com o gerenciamento de memória e referências de escopo indesejadas ao usar closures.

Você pode acompanhar o artigo acima usando o Visual Studio Code em nosso canal do YouTube. Por favor, confira também o canal do YouTube.

YouTube Video