ts-remote-data
Version:
Utility type for working with snapshot views of asynchronously-available data
208 lines (207 loc) • 8.13 kB
TypeScript
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;