withCleanup

function withCleanup<TClient>(
    client,
    cleanup,
): {
    [K in string | number | symbol]: (Omit<TClient, typeof dispose> &
        Disposable)[K];
};

Wraps a client with a cleanup function, making it Disposable.

Plugin authors can use this to register teardown logic (e.g. closing connections or clearing timers) that runs when the client is disposed. If the client already implements Symbol.dispose, the existing dispose logic is chained so that it runs after the new cleanup function.

Cleanups run in reverse order of registration, disposal is idempotent, and if more than one cleanup throws then the errors are aggregated into a SuppressedError chain. Runtimes that have not shipped explicit resource management are supported too, though a using declaration needs a Symbol.dispose polyfill there.

The return type is an ExtendedClient, which flattens the merged shape into a single object literal so chained calls do not accumulate nested intersections in editor tooltips and error messages.

Type Parameters

Type ParameterDescription
TClient extends objectThe type of the original client.

Parameters

ParameterTypeDescription
clientTClientThe client to wrap.
cleanup() => voidThe cleanup function to run when the client is disposed.

Returns

{ [K in string | number | symbol]: (Omit<TClient, typeof dispose> & Disposable)[K] }

A new client that extends TClient and implements Disposable.

Examples

Register a cleanup function in a plugin that opens a WebSocket connection.

function myPlugin() {
    return <T extends object>(client: T) => {
        const socket = new WebSocket('wss://api.example.com');
        return withCleanup(
            extendClient(client, { socket }),
            () => socket.close(),
        );
    };
}
 
// Build the client in the scope that should own it:
using client = createClient().use(myPlugin());
// `socket.close()` is called automatically when `client` goes out of scope.

Disposing without a using declaration

using requires explicit resource management, which Safari has not shipped as of Safari 27. Either dispose the client yourself, as below, or polyfill Symbol.dispose — installing the polyfill before any client is created, since a client registers its dispose method under whatever Symbol.dispose was at the time.

const client = createClient().use(myPlugin());
 
// Later, when the client is no longer needed:
client[Symbol.dispose]();
// `socket.close()` has now been called.

See

Remarks

See https://caniuse.com/mdn-javascript_builtins_disposablestack for platform availability of DisposableStack.

On this page