TypeScript Using Keyword and Explicit Resource Management

Memory leaks in production often stem from one error: developers acquire resources but fail to release them.

Database connections stay open after errors. File handles consume system resources. WebSocket clients stay connected to dead servers. This happens when you rely on manual cleanup in finally blocks.

The TypeScript using keyword solves this. It is part of the ECMAScript Explicit Resource Management proposal. It guarantees cleanup through the disposable pattern. When a resource leaves its scope, TypeScript runs its disposal method automatically.

You no longer need manual finally blocks. You no longer forget cleanup. You no longer leak connections.

How it works:

  • The using keyword ensures disposal when resources exit a scope.
  • Disposable resources implement Symbol.dispose for sync tasks or Symbol.asyncDispose for async tasks.
  • TypeScript turns using declarations into try-finally blocks with an automatic disposal stack.
  • This pattern prevents leaks caused by early returns, thrown exceptions, or forgotten code.

Use using for any resource with a set lifetime, such as database connections, file handles, locks, or timers.

The mechanism uses a disposal protocol. Objects implement a method keyed by Symbol.dispose. When the scope exits via normal completion, a return, or an exception, TypeScript calls that method.

Example of a file handle:

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

The disposal runs in a generated finally block. It runs even if the function throws an error or returns early.

TypeScript maintains a disposal stack. It disposes resources in reverse order of how you acquired them. This matches the natural dependency order for most cleanup logic.

For asynchronous cleanup, use await using. This requires the resource to implement Symbol.asyncDispose. TypeScript will await the returned Promise before continuing.

Three rules for success:

  • Do not throw exceptions inside your disposal methods. Catch and log errors internally instead.
  • Do not use async operations inside a synchronous Symbol.dispose method. Use Symbol.asyncDispose instead.
  • Remember that using declarations are block-scoped. A resource inside an if block disposes when that block ends, not when the function ends.

This pattern provides safety with negligible runtime cost.

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