export type Loadable = Init | Loading | Loaded | Failure type Init = { key: 'INIT' } export const init: Init = { key: 'INIT' } export function isInit(l: Loadable): l is Init { return l.key == 'INIT' } type Loading = { key: 'LOADING' } export const loading: Loading = { key: 'LOADING' } export function isLoading(l: Loadable): l is Loading { return l.key == 'LOADING' } type Loaded = { key: 'LOADED', value: T } export function loaded(value: T): Loaded { return { key: 'LOADED', value } } export function isLoaded(l: Loadable): l is Loaded { return l.key == 'LOADED' } type Failure = { key: 'FAILURE', error: string } export function failure(error: string): Failure { return { key: 'FAILURE', error } } export function isFailure(l: Loadable): l is Failure { return l.key == 'FAILURE' } export function map(loadable: Loadable, f: (value: A) => B): Loadable { if (loadable.key == 'LOADED') { return loaded(f(loadable.value)) } else { return loadable } } export function map2(l: [Loadable, Loadable], f: (a: A, b: B) => C): Loadable { const [l1, l2] = l if (l1.key == 'FAILURE') { return l1 } else if (l2.key == 'FAILURE') { return l2 } else if (l1.key == 'LOADING' || l2.key == 'LOADING') { return loading } else if (l1.key == 'INIT' || l2.key == 'INIT') { return init } else { return loaded(f(l1.value, l2.value)) } }