Skip to content

Instantly share code, notes, and snippets.

@janwirth
Last active January 6, 2020 06:22
Show Gist options
  • Save janwirth/c42889d60588e449dcbac138b7f617c7 to your computer and use it in GitHub Desktop.
Save janwirth/c42889d60588e449dcbac138b7f617c7 to your computer and use it in GitHub Desktop.
JS Remote Data Type with Typescript Typings
// Example: RemoteData<HttpError, VehicleList>
export type RemoteData<E, D> = NotAsked | Loading | Failure<E> | Success<D>;
// Example: RemoteDataMatchers<HttpError, VehicleList>
export interface RemoteDataMatchers<E, D, R> {
NotAsked: () => R;
Loading: () => R;
Failure: (E) => R;
Success: (D) => R;
}
export const notAsked: RemoteData<F, D>;
export const loading: RemoteData<F, D>;
export function success<F, D>(data: D): RemoteData<F, D>;
export function failure<F, D>(err: F): RemoteData<F, D>;
export type NotAsked = {
type: "NotAsked";
};
export type Loading = {
type: "Loading";
};
export type Failure<E> = {
type: "Failure";
error: E;
};
export type Success<D> = {
type: "Success";
data: D;
};
declare function match<E, D, R>(
matchers: RemoteDataMatchers<E, D, R>
): (remoteData: RemoteData<E, D>) => any;
declare function map<E, D>(
fn: (D) => D_
): (remoteData: RemoteData<E, D>) => RemoteData<E, D_>;
declare function isSuccess(rd: RemoteData<E, D>): boolean;
declare function isLoading(rd: RemoteData<E, D>): boolean;
declare function isFailure(rd: RemoteData<E, D>): boolean;
/**
* @typedef {import('./RemoteData').RemoteData<E, D>} RemoteData<E, D>
* @template E, D
*/
/**
* @typedef {import('./RemoteData').RemoteDataMatchers<E, D, R>} RemoteDataMatchers<E, D, R>
* @template E, D, R
*/
/**
* This function takes an object with a property of each state that remote data can be in.
* The corresping function will be called depending on what type of remmote data we pass in.
* @param {RemoteDataMatchers<E, D, R>} matchers
* @returns (remoteData: RemoteData<E,D,R>): R
* @template E, D, R
*/
export const match = matchers => remoteData => {
switch (remoteData.type) {
case "NotAsked":
return matchers.NotAsked();
case "Loading":
return matchers.Loading();
case "Failure":
return matchers.Failure(remoteData.err);
case "Success":
return matchers.Success(remoteData.data);
default:
return matchers.Loading();
}
};
export const map = fn => remoteData => {
switch (remoteData.type) {
case "Success":
return success(fn(remoteData.data));
default:
return remoteData;
}
};
/**
* @type RemoteData<E, D>
* @template E, D
*/
export const notAsked = { type: "NotAsked" };
/**
* @type RemoteData<E, D>
* @template E, D
*/
export const loading = { type: "Loading" };
/**
* @param {E} err
* @returns RemoteData<E, D>
* @template E, D
*/
export const failure = err => ({ type: "Failure", err });
/**
* @param {D} data
* @returns RemoteData<E, D>
* @template E, D
*/
export const success = data => ({ type: "Success", data });
/**
* @param {RemoteData<E, D>} rd
* @returns boolean
*/
export const isSuccess = rd => rd.type === "Success";
/**
* @param {RemoteData<E, D>} rd
* @returns boolean
*/
export const isLoading = rd => rd.type === "Loading";
/**
* @param {RemoteData<E, D>} rd
* @returns boolean
*/
export const isFailure = rd => rd.type === "Failure";
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment