Palavra-chave using do TypeScript e Gerenciamento Explícito de Recursos

Vazamentos de memória em produção geralmente surgem de um erro: desenvolvedores adquirem recursos, mas não conseguem liberá-los.

Conexões de banco de dados permanecem abertas após erros. Manipuladores de arquivos (file handles) consomem recursos do sistema. Clientes WebSocket permanecem conectados a servidores inativos. Isso acontece quando você depende de limpeza manual em blocos finally.

A palavra-chave using do TypeScript resolve isso. Ela faz parte da proposta de Gerenciamento Explícito de Recursos do ECMAScript. Ela garante a limpeza por meio do padrão disposable. Quando um recurso sai de seu escopo, o TypeScript executa seu método de descarte automaticamente.

Você não precisa mais de blocos finally manuais. Você não esquece mais a limpeza. Você não vaza mais conexões.

Como funciona:

  • A palavra-chave using garante o descarte quando os recursos saem de um escopo.
  • Recursos descartáveis implementam Symbol.dispose para tarefas síncronas ou Symbol.asyncDispose para tarefas assíncronas.
  • O TypeScript transforma declarações using em blocos try-finally com uma pilha de descarte automática.
  • Este padrão evita vazamentos causados por retornos antecipados, exceções lançadas ou código esquecido.

Use using para qualquer recurso com um tempo de vida definido, como conexões de banco de dados, manipuladores de arquivos, travas (locks) ou temporizadores (timers).

O mecanismo utiliza um protocolo de descarte. Os objetos implementam um método identificado por Symbol.dispose. Quando o escopo é encerrado por conclusão normal, um return ou uma exceção, o TypeScript chama esse método.

Exemplo de um manipulador de arquivo:

class FileHandle {
  private handle: number;

  constructor(path: string) {
    this.handle = openFileSync(path);
  }

  [Symbol.dispose]() {
    if (this.handle !== -1) {
      closeFileSync(this.handle);
      this.handle = -1;
    }
  }

  read(buffer: Buffer): number {
    return readSync(this.handle, buffer);
  }
}

function processFile(path: string) {
  using file = new FileHandle(path);
  const buffer = Buffer.alloc(1024);
  file.read(buffer);
}

O descarte é executado em um bloco finally gerado. Ele é executado mesmo se a função lançar um erro ou retornar antecipadamente.

O TypeScript mantém uma pilha de descarte. Ele descarta os recursos na ordem inversa à que foram adquiridos. Isso corresponde à ordem natural de dependência para a maioria das lógicas de limpeza.

Para limpeza assíncrona, use await using. Isso exige que o recurso implemente Symbol.asyncDispose. O TypeScript aguardará a Promise retornada antes de continuar.

Três regras para o sucesso:

  • Não lance exceções dentro de seus métodos de descarte. Em vez disso, capture e registre (log) os erros internamente.
  • Não use operações assíncronas dentro de um método Symbol.dispose síncrono. Use Symbol.asyncDispose em vez disso.
  • Lembre-se de que as declarações using têm escopo de bloco. Um recurso dentro de um bloco if é descartado quando esse bloco termina, não quando a função termina.

Este padrão oferece segurança com um custo de tempo de execução insignificante.

Fonte: https://dev.to/jsmanifest/typescript-using-keyword-and-explicit-resource-management-done-right-22pg