UNPKG

ts-remote-data

Version:

Utility type for working with snapshot views of asynchronously-available data

208 lines (207 loc) 8.13 kB
declare const NOT_ASKED: 'RemoteData::NOT_ASKED'; declare const LOADING: 'RemoteData::LOADING'; declare const FAILURE_PROPERTY = "RemoteData::FAILURE"; /** * A representation of an unsuccessful load of async data. Note that the * usage of `RemoteData` does not require that every failed data retrieval * update the state to be a failure; it is valid to preserve the last-seen * value instead. * * # Usage * Create an instance of this type using `RemoteData.failWith` to avoid * depending on the internal representation of the failure. */ export interface RemoteDataFailure { /** * Marker property that this object represents a `RemoteDataFailure`. * A namespaced string key is used for this to ensure compatibility with * Redux stores while minimizing the risk of collisions with user-defined * types. */ [FAILURE_PROPERTY]: true; /** * The error encountered during data loading. */ error: unknown; } /** * A snapshot point-in-time view of some data that will be asynchronously ready. * This can be used to represent the current state of a `Promise` in places that * require plain objects, or in places that need a view of an async operation * they are not a party to. * * Importantly, there is no runtime difference between a `RemoteData<T>` in the * "ready" state and a bare `T`. Therefore, any component can be converted from * accepting a `T` to accepting a `RemoteData<T>` without changing component * consumers. * * This is inspired by Elm's `RemoteData` enum type. */ declare type RemoteData<T> = typeof NOT_ASKED | typeof LOADING | RemoteDataFailure | T; declare const RemoteData: { /** * Initial state for remote data. The client has not yet sent a request * to the server. */ NOT_ASKED: "RemoteData::NOT_ASKED"; /** * State when the client has started a request but not yet received a * response. */ LOADING: "RemoteData::LOADING"; /** * Create a failure object with no associated data. */ fail: () => RemoteDataFailure; /** * State when the client has received a conclusive response from the server * that was an error. If a non-initial update fails, it is not required * that consumers of `RemoteData` overwrite the previous data with the * failure, but they may do so if it is contextually appropriate. */ failWith: (error: unknown) => RemoteDataFailure; /** * State when the client has received a conclusive response from the server * that was an error. If a non-initial update fails, it is not required * that consumers of `RemoteData` overwrite the previous data with the * failure, but they may do so if it is contextually appropriate. */ isFailure: <T>(rd: RemoteData<T>) => rd is RemoteDataFailure; /** * Check if some remote data is available. This function acts as a type * guard; if it returns `true` then `remoteData` can now be used as a `T`. */ isReady: <T_1>(remoteData: RemoteData<T_1>) => remoteData is T_1; /** * Check if some remote data is ready or in the failure state. The term * "settled" comes from Promises, where it means "fulfilled or rejected". * This function acts as a type guard. */ isSettled: <T_2>(remoteData: RemoteData<T_2>) => remoteData is RemoteDataFailure | T_2; /** * Check if some remote data is ready; if so return it, otherwise return * `undefined`. This can be used with `||` to create a short-circuiting * default. */ asReady: <T_3>(remoteData: RemoteData<T_3>) => T_3 | undefined; /** * Check if some remote data has failed; if so, return the failure, * otherwise return `undefined`. */ asFailure: <T_4>(rd: RemoteData<T_4>) => RemoteDataFailure | undefined; /** * Check if some remote data has settled; if so return the value or the * failure. The term "settled" come from Promises, where it means * "fulfilled or rejected". */ asSettled: <T_5>(rd: RemoteData<T_5>) => RemoteDataFailure | T_5 | undefined; /** * Get the value of some remote data if available, otherwise return a * specified fallback value. */ getOr: <T_6>(remoteData: RemoteData<T_6>, fallback: T_6) => T_6; /** * Get the value of some remote data if available, otherwise throw an * exception. * * # Usage * When the caller is certain that code is only reachable after some * remote data is available, the value can be used by writing * `RemoteData.unwrap(rd).someRdMethod();` * * @throws `Error` if `remoteData` is not in the ready state. */ unwrap: <T_7>(remoteData: RemoteData<T_7>) => T_7; /** * Return the first `RemoteData` in the 'ready' state. * This is used to kick off an initial data request but to avoid flattening * data on subsequent updates, or to restart a query after a loading failure. */ or: <T_8>(lhs: RemoteData<T_8>, rhs: RemoteData<T_8>) => RemoteData<T_8>; /** * Apply a transform function to a remote data if it is ready, otherwise * return the current state. */ map: <T_9, U>(remoteData: RemoteData<T_9>, mapFn: (value: T_9) => U) => RemoteData<U>; /** * Recover from a failure by using a fallback value, otherwise * returning the passed in remote data. */ recover: <T_10>(remoteData: RemoteData<T_10>, fallback: T_10) => RemoteData<T_10>; /** * Checks if all the input `RemoteData` are ready, and if so * returns an array of the values. Otherwise, it returns a single * status, in the following priority order: * * 1. Failure * 2. `RemoteData.LOADING` * 3. `RemoteData.NOT_ASKED` */ all: typeof all; }; /** * Map `T` such that each field can be loaded remotely. Note that due to the * way `RemoteData` is defined, this transformation is idempotent: * * ```typescript * RemoteData<T> === RemoteData<RemoteData<T>> * ``` * * This may be surprising when compared to monadic types in other languages. */ export declare type RemotePerField<T> = { [k in keyof T]: RemoteData<T[k]>; }; /** * Checks if all the input `RemoteData` are ready, and if so * returns an array of the values. Otherwise, it returns a single * status, in the following priority order: * * 1. Failure * 2. `RemoteData.LOADING` * 3. `RemoteData.NOT_ASKED` */ declare function all<T1, T2, T3, T4, T5>(t1: RemoteData<T1>, t2: RemoteData<T2>, t3: RemoteData<T3>, t4: RemoteData<T4>, t5: RemoteData<T5>): RemoteData<[T1, T2, T3, T4, T5]>; /** * Checks if all the input `RemoteData` are ready, and if so * returns an array of the values. Otherwise, it returns a single * status, in the following priority order: * * 1. Failure * 2. `RemoteData.LOADING` * 3. `RemoteData.NOT_ASKED` */ declare function all<T1, T2, T3, T4>(t1: RemoteData<T1>, t2: RemoteData<T2>, t3: RemoteData<T3>, t4: RemoteData<T4>): RemoteData<[T1, T2, T3, T4]>; /** * Checks if all the input `RemoteData` are ready, and if so * returns an array of the values. Otherwise, it returns a single * status, in the following priority order: * * 1. Failure * 2. `RemoteData.LOADING` * 3. `RemoteData.NOT_ASKED` */ declare function all<T1, T2, T3>(t1: RemoteData<T1>, t2: RemoteData<T2>, t3: RemoteData<T3>): RemoteData<[T1, T2, T3]>; /** * Checks if all the input `RemoteData` are ready, and if so * returns an array of the values. Otherwise, it returns a single * status, in the following priority order: * * 1. Failure * 2. `RemoteData.LOADING` * 3. `RemoteData.NOT_ASKED` */ declare function all<T1, T2>(t1: RemoteData<T1>, t2: RemoteData<T2>): RemoteData<[T1, T2]>; /** * Checks if all the input `RemoteData` are ready, and if so * returns an array of the values. Otherwise, it returns a single * status, in the following priority order: * * 1. Failure * 2. `RemoteData.LOADING` * 3. `RemoteData.NOT_ASKED` * * @param args An array or tuple of `RemoteData` values. */ declare function all<T>(...args: RemoteData<T>[]): RemoteData<T[]>; export default RemoteData;