type LazyExecutor = (() => Promise) | (() => T); // Note: static methods are not implemented, as we do not need them yet. `.withResolvers()` might be useful, though. export class LazyPromise implements Promise { #executor: LazyExecutor; constructor(executor: LazyExecutor) { this.#executor = executor; } #cached: Promise | undefined; async #getCached(): Promise { // Note that we use `Promise.resolve(…)` to preserve correct semantics in case the type of `T` is `undefined` or `null` (which could be returned by a synchronous executor). // biome-ignore lint/suspicious/noAssignInExpressions: Caching pattern return (this.#cached ??= Promise.resolve(this.#executor())); } // Type signature from TypeScript // biome-ignore lint/suspicious/noThenProperty: We're implementing the `Promise` API! async then( onfulfilled?: | ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: // biome-ignore lint/suspicious/noExplicitAny: We're implementing the `Promise` API! ((reason: any) => TResult2 | PromiseLike) | undefined | null, ): Promise { return this.#getCached().then(onfulfilled, onrejected); } // Type signature from TypeScript catch( onrejected?: // biome-ignore lint/suspicious/noExplicitAny: We're implementing the `Promise` API! ((reason: any) => TResult | PromiseLike) | undefined | null, ): Promise { return this.#getCached().catch(onrejected); } // Type signature from TypeScript async finally(onfinally?: (() => void) | undefined | null): Promise { return this.#getCached().finally(onfinally); } get [Symbol.toStringTag](): string { return "LazyPromise"; } }