La palabra clave using de TypeScript y la gestión explícita de recursos

Las fugas de memoria en producción suelen derivar de un error: los desarrolladores adquieren recursos pero no logran liberarlos.

Las conexiones a bases de datos permanecen abiertas tras un error. Los descriptores de archivos consumen recursos del sistema. Los clientes WebSocket permanecen conectados a servidores inactivos. Esto ocurre cuando se depende de la limpieza manual en bloques finally.

La palabra clave using de TypeScript resuelve esto. Forma parte de la propuesta de Gestión Explícita de Recursos de ECMAScript. Garantiza la limpieza mediante el patrón de objetos desechables (disposable pattern). Cuando un recurso sale de su ámbito (scope), TypeScript ejecuta su método de disposición automáticamente.

Ya no necesitas bloques finally manuales. Ya no olvidas la limpieza. Ya no sufres fugas de conexiones.

Cómo funciona:

  • La palabra clave using asegura la disposición cuando los recursos salen de un ámbito.
  • Los recursos desechables implementan Symbol.dispose para tareas síncronas o Symbol.asyncDispose para tareas asíncronas.
  • TypeScript convierte las declaraciones using en bloques try-finally con una pila de disposición automática.
  • Este patrón evita fugas causadas por retornos tempranos, excepciones lanzadas o código olvidado.

Usa using para cualquier recurso con un tiempo de vida definido, como conexiones a bases de datos, descriptores de archivos, bloqueos (locks) o temporizadores (timers).

El mecanismo utiliza un protocolo de disposición. Los objetos implementan un método identificado por Symbol.dispose. Cuando el ámbito finaliza mediante una ejecución normal, un return o una excepción, TypeScript llama a ese método.

Ejemplo de un descriptor de archivo:

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);
}

La disposición se ejecuta en un bloque finally generado. Se ejecuta incluso si la función lanza un error o retorna prematuramente.

TypeScript mantiene una pila de disposición. Libera los recursos en el orden inverso al que fueron adquiridos. Esto coincide con el orden de dependencia natural para la mayoría de la lógica de limpieza.

Para la limpieza asíncrona, usa await using. Esto requiere que el recurso implemente Symbol.asyncDispose. TypeScript esperará (await) a la Promesa devuelta antes de continuar.

Tres reglas para el éxito:

  • No lances excepciones dentro de tus métodos de disposición. En su lugar, captura y registra (log) los errores internamente.
  • No uses operaciones asíncronas dentro de un método Symbol.dispose síncrono. Usa Symbol.asyncDispose en su lugar.
  • Recuerda que las declaraciones using tienen ámbito de bloque (block-scoped). Un recurso dentro de un bloque if se libera cuando ese bloque termina, no cuando la función termina.

Este patrón proporciona seguridad con un coste de ejecución (runtime) insignificante.

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