stdlazy
Version:
Flexible and debuggable lazy primitive.
166 lines • 8.36 kB
TypeScript
import { LazyAsync, LazyInfo, Pullable, Pulled, PulledAwaited, type _IterationType } from "./types";
export declare const methodName: unique symbol;
export declare const ownerInstance: unique symbol;
/**
* A TypeScript-first lazy evaluation primitive. A {@link Pullable} that will only evaluate its
* initializer function when the {@link pull} method is called.
*
* The initializer can return another {@link Lazy}, which will be chained like a promise.
*/
export declare class Lazy<T> implements Pullable<T>, Iterable<_IterationType<T>>, AsyncIterable<_IterationType<T>> {
/** The cached value or error, stored from a previous execution of the initializer. */
private _cached?;
private _desc;
private _info;
get info(): Readonly<LazyInfo>;
/**
* The initializer function that will be called to construct the value. It will be cleared after
* the value is constructed, unless `LAZY_NOCLEAR` is set.
*/
private _init;
/** Has the initializer finished executing? */
get isReady(): boolean;
[Symbol.iterator](): Iterator<any>;
[Symbol.asyncIterator](): AsyncIterator<any>;
private constructor();
static create<T>(f: () => T): Lazy<T>;
private _makeDescription;
/** Returns a short description of the Lazy value and its state. */
toString(): string;
/**
* Evaluates this {@link Lazy} instance, flattening any nested {@link Lazy} or {@link Promise}
* types.
*
* @returns The value produced by the initializer, after flattening any nested {@link Lazy} or
* {@link Promise} instances.
* @throws The error thrown during initialization, if any.
*/
pull(): Pulled<T>;
get [Symbol.toStringTag](): string;
/**
* Creates a new {@link Lazy} primitive that, when pulled, will pull **this** and return its
* result, projected using the given function. If the Lazy primitive is async, the projection
* will receive the awaited value.
*
* @example
* // sync projectionL
* const sync = lazy(() => "hello").map(x => `${x} world`) satisfies Lazy<string>
* expect(sync.pull()).toBe("hello world")
*
* // sync projection on async lazy:
* const async = lazy(async () => [1]).map(x => [...x, 2]) satisfies LazyAsync<number[]>
* await expect(async.pull()).resolves.toBe(2)
*
* // async projection on sync lazy:
* const nested = lazy(() => 1).map(async x => x + 1) satisfies LazyAsync<number>
* await expect(nested.pull()).resolves.toBe(2)
*
* // async projection on async lazy:
* const asyncToAsync = lazy(async () => 1).map(
* async x => x + 1
* ) satisfies LazyAsync<number>
* await expect(asyncToAsync.pull()).resolves.toBe(2)
*
* @param projection The function to apply to the value of the Lazy primitive. It will flatten
* any nested {@link Lazy} and {@link Promise} instances.
* @summary
* Projects the result of this {@link Lazy} primitive using the given function.
* @see {@link Array.map} for a similar method on arrays.
* @see {@link Promise.then} for a similar method on promises.
* @see {@link Lazy.do} for a similar method that doesn't change the result.
*/
map<S, R>(this: LazyAsync<S>, projection: (value: PulledAwaited<S>) => Promise<LazyAsync<R>>): LazyAsync<R>;
map<S, Y>(this: LazyAsync<S>, projection: (value: PulledAwaited<S>) => Promise<LazyAsync<Y>>): LazyAsync<Y>;
map<S, X>(this: LazyAsync<S>, projection: (value: PulledAwaited<S>) => Promise<Lazy<X>>): LazyAsync<X>;
map<S, X>(this: LazyAsync<S>, projection: (value: PulledAwaited<S>) => Promise<X>): LazyAsync<X>;
map<S, X>(this: LazyAsync<S>, projection: (value: PulledAwaited<S>) => LazyAsync<X>): LazyAsync<X>;
map<S, R>(this: LazyAsync<S>, f: (value: PulledAwaited<S>) => Lazy<R>): LazyAsync<R>;
map<S, R>(this: LazyAsync<S>, f: (value: PulledAwaited<S>) => R): LazyAsync<R>;
map<Y>(projection: (value: PulledAwaited<T>) => Promise<LazyAsync<Y>>): LazyAsync<Y>;
map<X>(projection: (value: PulledAwaited<T>) => Promise<Lazy<X>>): LazyAsync<X>;
map<X>(projection: (value: PulledAwaited<T>) => Promise<X>): LazyAsync<X>;
map<R>(projection: (value: PulledAwaited<T>) => Lazy<R>): Lazy<R>;
map<R>(projection: (value: PulledAwaited<T>) => R): Lazy<R>;
/**
* Creates a new {@link Lazy} primitive that, when pulled, will pull **this** and apply the given
* callback to the result. The new {@link Lazy} will still return the same value as **this**,
* only waiting for the handler to finish first.
*
* @example
* const lazy = lazy(() => 1).do(x => console.log(x)) satisfies Lazy<number>
* expect(lazy.pull()).toBe(1) // Logs "1" to the console as a side effect.
* const wait30 = lazy(() => 1).do(
* async x => new Promise(r => setTimeout(r, 30))
* ) satisfies Lazy<number>
* await expect(wait30.pull()).resolves.toBe(1) // Waits 30ms before returning 1.
*
* @param callback The callback
* @summary Applies the given callback to the result of this {@link Lazy} primitive.
*/
do<S>(this: LazyAsync<S>, callback: (value: S) => any | Lazy<any> | Promise<any> | Promise<LazyAsync<any>> | LazyAsync<any>): LazyAsync<S>;
do<T>(this: Lazy<T>, callback: (value: PulledAwaited<T>) => Promise<any> | LazyAsync<any>): LazyAsync<T>;
do<T>(this: Lazy<T>, callback: (value: PulledAwaited<T>) => Lazy<any>): Lazy<T>;
do<T>(this: Lazy<T>, callback: (value: PulledAwaited<T>) => any): Lazy<T>;
/**
* Zips **this** {@link Lazy} primitive with one or more others, returning a new {@link Lazy}
* that, when pulled, will pull all of them and return an array with the results. If any
* primitive involved is async, the new {@link Lazy} will also be async.
*
* @example
* const a = lazy(() => 1).zip(lazy(() => 2)) satisfies Lazy<[number, number]>
* expect(a.pull()).toEqual([1, 2])
*
* const b = lazy(async () => 1).zip(lazy(() => 2)) satisfies LazyAsync<[number, number]>
* await expect(b.pull()).resolves.toEqual([1, 2])
*
* @param others One or more {@link Lazy} primitives to zip with **this**.
* @summary Turns multiple lazy values into a single lazy value producing an array.
*/
zip<Others extends readonly [Pullable<unknown>, ...Pullable<unknown>[]]>(...others: Others): LazyAsync<any> extends [this, ...Others][number] ? LazyAsync<[
PulledAwaited<T>,
...{
[K in keyof Others]: PulledAwaited<Others[K]>;
}
]> : Lazy<[
Pulled<T>,
...{
[K in keyof Others]: Pulled<Others[K]>;
}
]>;
/**
* Takes an key-value object with {@link Lazy} values and returns a new {@link Lazy} that, when
* pulled, will pull all of them and return an object with the same keys, but with the values
* replaced by the pulled results. If any of the values are async, the new {@link Lazy} will also
* be async.
*
* The value of **this** {@link Lazy} will be available under the key `"this"`.
*
* @example
* const self = lazy(() => 1).assemble({
* a: lazy(() => 2),
* b: lazy(() => 3)
* })
* expect(self.pull()).toEqual({ this: 1, a: 2, b: 3 })
*
* const asyncSelf = lazy(async () => 1).assemble({
* a: lazy(() => 2),
* b: lazy(() => 3)
* })
* await expect(asyncSelf.pull()).resolves.toEqual({ this: 1, a: 2, b: 3 })
*
* @param assembly An object with {@link Lazy} values.
* @returns A new {@link Lazy} primitive that will return an object with the same keys as the
* input object, plus the key `"this"`, with the pulled results.
* @summary Converts an object of {@link Lazy} values into a {@link Lazy} value producing an object.
*/
assemble<X extends Record<keyof X, Pullable<unknown>>>(assembly: X): LazyAsync<any> extends X[keyof X] | this ? LazyAsync<{
[K in keyof X]: PulledAwaited<X[K]>;
} & {
this: PulledAwaited<T>;
}> : Lazy<{
[K in keyof X]: Pulled<X[K]>;
} & {
this: Pulled<T>;
}>;
}
//# sourceMappingURL=lazy.d.ts.map