Question presented to candidate:
"A service needs a primary database connection, a replica and a timer at startup. If the second step fails, the first must be closed; if everything succeeds, the caller should own all three and close them together. How would you write that with DisposableStack, and how do you type the pieces?"
What a strong answer should cover:
DisposableStackcollects cleanup work and is itselfDisposable:use(value)registers a disposable,adopt(value, onDispose)wraps a value that has no dispose method,defer(fn)registers a callback; everything runs in reverse order ondispose()- The setup pattern:
using stack = new DisposableStack(), register each resource right after acquiring it, and on success callstack.move()to transfer ownership out, so a failure part-way rolls back only what was opened AsyncDisposableStackdoes the same withdisposeAsync()/Symbol.asyncDispose, accepts both sync and async disposables, and is used withawait using- Error aggregation: when several cleanups throw, the errors are chained in
SuppressedErrorobjects (error= the later failure,suppressed= the earlier);disposedbecomestrueeven then - The types:
use<T extends Disposable | null | undefined>(value: T): Tandadopt<T>(value: T, onDispose: (value: T) => void): Treturn the exact type passed in; a composed class holds a stack in a private field and implementsDisposableby delegating - Runtime support: built in only in newer runtimes (for example Node.js 24 and Chrome 134 per MDN data); otherwise a polyfill, since
lib: esnext.disposableonly provides types
Clarifying questions expected:
- Are any of the resources released asynchronously (connections usually are)?
- Which runtimes must this run on, and is a polyfill acceptable?
Code / implementation expected: Yes β an acquire-several-resources function that rolls back on failure and returns a disposable bundle on success.