typescript / expert
Snippet
Deterministic Resource Cleanup with `await using`
`await using` (TC39 Explicit Resource Management, supported since TypeScript 5.2) binds an async-disposable to a block scope and invokes its `Symbol.asyncDispose` method when the scope exits — even when an exception escapes. This eliminates the try/finally boilerplate that traditionally protected connection pools, file handles and locks, and guarantees ordered teardown across multiple resources in LIFO order. Combined with `AsyncDisposable`, you get RAII semantics that survive async control flow.
snippet.ts
typescript
1
2
3
4
5
6
7
8
9
10
11
12
class DbConnection implements AsyncDisposable {constructor(public readonly id: string) {}async query(sql: string): Promise<unknown[]> { return []; }async [Symbol.asyncDispose](): Promise<void> {console.log(`closing ${this.id}`);}}async function fetchUsers(): Promise<unknown[]> {await using conn = new DbConnection("main");return conn.query("SELECT * FROM users");}
Breakdown
1
class DbConnection implements AsyncDisposable {
Declares conformance to the AsyncDisposable contract.
2
async [Symbol.asyncDispose](): Promise<void> {
Cleanup hook the runtime awaits when the binding leaves scope.
3
await using conn = new DbConnection("main");
Binds conn for the rest of the block; disposal runs even on throw.
4
return conn.query("SELECT * FROM users");
The pending promise is awaited before disposal because of await using.