Function: asDisposable()
API / @xmachines/core / utils / asDisposable
Call Signature
function asDisposable<F>(release): F & DisposeMember;Defined in: packages/core/src/utils/disposable.ts:126
Makes one release function a Disposable. The function is its own release.
The function comes back as itself, with DISPOSE added. Nothing wraps it, so
the identity of the release holds: a caller that compares two releases, or that puts
one in a Set, sees what it saw before. A caller that runs the release keeps that
code, and a caller inside one scope writes using:
// The explicit form.const stop = watchAtom(count, render);stop();
// The scoped form. The scope releases it, and an exception releases it too.{ using stop = asDisposable(watchAtom(count, render)); render(count.get());}A value that is a Disposable already comes back unchanged. The function adds the key
only where it is absent, so a second call changes nothing. The one exception is a
well-known key that a late polyfill installed: the second call stores the release there.
The release must be idempotent. A scope releases the value one time, but a caller that runs the function AND lets the scope end runs it two times. Every release of this workspace accepts that.
The release must be synchronous. Disposable and not AsyncDisposable: a
Disposable serves using AND await using, because await using falls back to
Symbol.dispose, while an AsyncDisposable raises a TypeError under a plain using
and forces every holder into an async function. The void | undefined bound rejects
an async release: a plain () => void bound accepts async () => {}, because
TypeScript lets a function that returns a value stand where one that returns void
is wanted. The scope would then ignore the promise, and nothing could await the work.
The bound holds where the compiler knows the return type. It cannot hold where the
caller erased it: a release typed any, such as a bare vi.fn(), satisfies every
constraint. Write the release inline, or type it, and the bound applies.
using needs Node 24 or a browser with Explicit Resource Management, and it needs
"lib": ["ESNext"] of the consumer. See DisposeKey for a consumer without it.
Type Parameters
| Type Parameter |
|---|
F extends () => void | undefined |
Parameters
| Parameter | Type | Description |
|---|---|---|
release | F | The release to publish. It must return nothing. The function is changed in place, so pass a function that this caller owns: a shared or module-level function becomes a Disposable for every other holder of it. |
Returns
F & DisposeMember
The same function, now also a Disposable.
Throws
TypeError - When the function is frozen or is otherwise not extensible, and a dispose key holds no member yet.
See
Call Signature
function asDisposable<T, R>(target, release): T & DisposeMember;Defined in: packages/core/src/utils/disposable.ts:157
Makes one object a Disposable, with a release that the caller names.
The object comes back as itself, with DISPOSE added, so a caller that compares two objects sees what it saw before.
An object that is a Disposable already, with a dispose member own or inherited,
makes the call throw. The scope would run that member and never the release that the
caller names, so a teardown would go missing without a sign. A second call with the
SAME release is not a conflict: it returns the object, and it writes only a well-known
key that a late polyfill left empty.
The rules of the function form apply to the release: it must be idempotent, and it must be synchronous.
Type Parameters
| Type Parameter |
|---|
T extends object |
R extends void | undefined |
Parameters
| Parameter | Type | Description |
|---|---|---|
target | T | The object to publish. It is changed in place, so pass an object that this release owns. |
release | () => R | The release that the scope runs. It must return nothing. |
Returns
T & DisposeMember
The same object, now also a Disposable.
Throws
DisposeConflictError - When the object has a dispose member already, and that member is not the named release.
Throws
TypeError - When the object is frozen or is otherwise not extensible, and a dispose key does not hold the release yet.
Example
const subscription = selection.subscribe(render);using disposable = asDisposable(subscription, () => subscription.unsubscribe());