← Tutti gli articoli

TypeScript 5.2: la keyword `using` e la fine dei try/finally infiniti

19 September 20232 min di lettura

TypeScript 5.2 introduce `using` e `await using`, anticipando la proposta ECMAScript Explicit Resource Management. Una piccola rivoluzione per chi gestisce file, connessioni e lock.

Il 24 agosto 2023 Microsoft ha rilasciato TypeScript 5.2 con una novità che sembra piccola ma che, una volta provata, è difficile lasciare: la keyword using. Anticipa la proposta Explicit Resource Management di ECMAScript (oggi allo stage 3) ed elimina una fonte cronica di bug nei progetti Node.

Il problema: chi chiude la risorsa?

Tutti abbiamo scritto codice come questo:

const file = await fs.open("data.txt");
try {
  // usa il file
} finally {
  await file.close();
}

Funziona, ma è verboso. Quando le risorse diventano due, tre o quattro, gli annidamenti di try/finally diventano un labirinto. E se qualcuno dimentica un finally, la risorsa rimane aperta finché non interviene il garbage collector — o non interviene affatto.

La soluzione: using

Con TypeScript 5.2 si scrive così:

using file = await fs.open("data.txt");
// uso il file
// alla fine dello scope viene chiamato file[Symbol.dispose]()

Il runtime invoca Symbol.dispose in modo automatico all'uscita dallo scope, che sia per ritorno normale, eccezione o break. Per le risorse asincrone esiste await using, che chiama Symbol.asyncDispose.

Quando ci serve davvero

Negli script di onboarding clienti che scriviamo regolarmente, capita di gestire connessioni a database, lock su file di configurazione e session su servizi esterni tutti nello stesso flusso. Con using il codice diventa lineare e impossibile da dimenticare. Anche per i Server Actions di Next.js abbiamo iniziato a creare wrapper che dispongono automaticamente di transazioni Prisma.

function transaction() {
  const tx = prisma.$begin();
  return {
    tx,
    [Symbol.asyncDispose]: () => tx.commit(),
  };
}

await using t = transaction();
await t.tx.user.update(...);
// commit automatico

Cosa serve per usarlo

Il target di compilazione deve supportare Symbol.dispose. TypeScript fa polyfill per i target più vecchi tramite l'helper __addDisposableResource. Su Node.js servono almeno la 20.x con flag o, ancora meglio, la 22 LTS che è arrivata nel 2024. Per i runtime più recenti (Bun, Deno) il supporto è già nativo.

Vale la migrazione?

Non è una feature che giustifica un refactoring di massa. È però una di quelle che, una volta entrata nelle mani del team, riduce silenziosamente i bug. La nostra regola: using obbligatorio per qualunque risorsa con open/close, connect/disconnect, lock/unlock. Per il resto, lasciamo stare.