/** * Anything with a .dispose() method is a disposable object, and implements the IDisposable interface. */ export interface IDisposable { dispose(): void; } /** * Anything with `.autoDispose()` can be the owner of a disposable object. This is a type-specific * class that can only own a disposable object of type T. */ export interface IDisposableOwnerT { autoDispose(obj: T): void; } /** * Type that can own an object of any disposable type. */ export interface IDisposableOwner { autoDispose(obj: IDisposable): void; } /** * The static portion of class Disposable. */ export interface IDisposableCtor { new (...args: CtorArgs): Derived; create any>(this: T, owner: IDisposableOwnerT> | null, ...args: ConstructorParameters): InstanceType; } /** * Base class for disposable objects that can own other objects. * * For background and motivation, see [Disposables](/dispose). * * `Disposable` is a class for components that need cleanup (e.g. maintain DOM, listen to events, * subscribe to anything). It provides a `.dispose()` method that should be called to destroy the * component, and `.onDispose()` / `.autoDispose()` methods that the component should use to take * responsibility for other pieces that require cleanup. * * To define a disposable class: * ```ts * class Foo extends Disposable { ... } * ``` * * To create `Foo`: * ```ts * const foo = Foo.create(owner, ...args); * ``` * This is better than `new Foo` for two reasons: * 1. If `Foo`'s constructor throws an exception, any disposals registered in that constructor * before the exception are honored. * 2. It ensures you specify the owner of the new instance (but you can use null to skip it). * * In `Foo`'s constructor (or rarely methods), take ownership of other Disposable objects: * ```ts * this.bar = Bar.create(this, ...); * ``` * * For objects that are not instances of Disposable but have a .dispose() methods, use: * ```ts * this.bar = this.autoDispose(createSomethingDisposable()); * ``` * * To call a function on disposal (e.g. to add custom disposal logic): * ```ts * this.onDispose(() => this.myUnsubscribeAllMethod()); * this.onDispose(this.myUnsubscribeAllMethod, this); * ``` * * To mark this object to be wiped out on disposal (i.e. set all properties to null): * ```ts * this.wipeOnDispose(); * ``` * See the documentation of that method for more info. * * To dispose Foo directly: `foo.dispose()`. * * To determine if an object has already been disposed: `foo.isDisposed()`. * * If you need to replace an owned object, or release, or dispose it early, use a * [`Holder`](#Holder) or [`MultiHolder`](#MultiHolder). * * If creating your own class with a `dispose()` method, do NOT throw exceptions from `dispose()`. * These cannot be handled properly in all cases. * * Using a parametrized (generic) class as a Disposable is tricky. E.g. * ```ts * class Bar extends Disposable { ... } * // Bar.create(...) <-- doesn't work * // Bar.create(...) <-- doesn't work * // Bar.create(...) <-- works, but with {} for Bar's type parameters * ``` * * The solution is to expose the constructor type using a helper method: * ```ts * class Bar extends Disposable { * // Note the tuple below which must match the constructor parameters of Bar. * public static ctor(): IDisposableCtor, [U, boolean]> { return this; } * constructor(a: T, b: boolean) { ... } * } * Bar.ctor().create(...) // <-- works, creates Bar, and does type-checking! * ``` */ export declare abstract class Disposable implements IDisposable, IDisposableOwner { /** * Create Disposable instances using `Class.create(owner, ...)` rather than `new Class(...)`. * * This reminds you to provide an owner, and ensures that if the constructor throws an * exception, `dispose()` gets called to clean up the partially-constructed object. * * Owner may be `null` if you intend to ensure disposal some other way. */ static create any>(this: T, owner: IDisposableOwnerT> | null, ...args: ConstructorParameters): InstanceType; private _disposalList; constructor(); /** Take ownership of `obj`, and dispose it when `this.dispose()` is called. */ autoDispose(obj: T): T; /** Call the given callback when `this.dispose()` is called. */ onDispose(callback: (this: T) => void, context?: T): IDisposable; /** * Wipe out this object when it is disposed, i.e. set all its properties to null. It is * recommended to call this early in the constructor. * * This makes disposal more costly, but has certain benefits: * * - If anything still refers to the object and uses it, we'll get an early error, rather than * silently keep going, potentially doing useless work (or worse) and wasting resources. * * - If anything still refers to the object (even without using it), the fields of the object * can still be garbage-collected. * * - If there are circular references involving this object, they get broken, making the job * easier for the garbage collector. * * The recommendation is to use it for complex, longer-lived objects, but to skip for objects * which are numerous and short-lived (and less likely to be referenced from unexpected places). */ wipeOnDispose(): void; /** * Returns whether this object has already been disposed. */ isDisposed(): boolean; /** * Clean up `this` by disposing all owned objects, and calling `onDispose()` callbacks, in reverse * order to that in which they were added. */ dispose(): void; /** * Wipe out this object by setting each property to null. This is helpful for objects that are * disposed and should be ready to be garbage-collected. */ private _wipeOutObject; } /** * Holder keeps a single disposable object. If given responsibility for another object using * `holder.autoDispose()` or `Foo.create(holder, ...)`, it automatically disposes the currently held * object. It also disposes it when the holder itself is disposed. * * If the object is an instance of `Disposable`, the holder will also notice when the object gets * disposed from outside of it, in which case the holder will become empty again. * * If you need a container for multiple objects and dispose them all together, use a `MultiHolder`: * * :::info Example * ```ts * this._holder = Holder.create(this); * Bar.create(this._holder, 1); // creates new Bar(1), assuming it's a Disposable * Bar.create(this._holder, 2); // creates new Bar(2) and disposes previous object * this._holder.clear(); // disposes contained object * this._holder.release(); // releases contained object * ``` * ::: */ export declare class Holder implements IDisposable, IDisposableOwner { /** `Holder.create(owner)` creates a new `Holder`. */ static create(owner: IDisposableOwnerT> | null): Holder; /** @internal */ protected _owned: T | null; private _disposalListener; /** Take ownership of a new object, disposing the previously held one. */ autoDispose(obj: T): T; /** Releases the held object without disposing it, emptying the holder. */ release(): IDisposable | null; /** Disposes the held object and empties the holder. */ clear(): void; /** Returns the held object, or null if the Holder is empty. */ get(): T | null; /** Returns whether the Holder is empty. */ isEmpty(): boolean; /** When the holder is disposed, it disposes the held object if any. */ dispose(): void; /** Stop listening for the disposal of this._owned. */ private _unlisten; private _onOutsideDispose; } /** * `MultiHolder` keeps multiple disposable objects. It disposes all held object when the holder * itself is disposed. It's actually nothing more than the `Disposable` base class itself, just * exposed with a clearer name that describes its purpose. * * :::info Example * ```ts * this._mholder = MultiHolder.create(null); * Bar.create(this._mholder, 1); // create new Bar(1) * Bar.create(this._mholder, 2); // create new Bar(2) * this._mholder.dispose(); // disposes both objects * ``` * ::: */ export declare class MultiHolder extends Disposable { } /** * Sets owner of obj (i.e. calls owner.autoDispose(obj)) unless owner is null. Returns obj. */ export declare function setDisposeOwner(owner: IDisposableOwnerT | null, obj: T): T;