-
-
Save romgrk/4466f7043774850ada532904a77bedf1 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Result from './Result' | |
import { Loader } from './useLoader' | |
enum State { | |
UNSET, | |
LOADING, | |
/** Or "LOADED" */ | |
OK, | |
ERROR, | |
} | |
export default class AsyncResult<T> { | |
state: State | |
value: T | undefined | |
error: Error | undefined | |
static Unset() { return new Unset() } | |
static Loading() { return new Loading() } | |
static Ok<T>(data: T) { return new Ok<T>(data) } | |
static Err(error: Error) { return new Err(error) } | |
static from<S>(result: Result<S>) { | |
return result.ok ? | |
AsyncResult.Ok(result.unwrap()) : | |
AsyncResult.Err(result.unwrapErr()) | |
} | |
static fromOptional<S>(value?: S): AsyncResult<S> { | |
return value !== undefined ? | |
AsyncResult.Ok(value) : | |
AsyncResult.Unset() | |
} | |
static fromLoader<S>(loader: Loader<S>): AsyncResult<S> { | |
if (loader.data.isSome()) | |
return AsyncResult.Ok(loader.data.unwrap()) | |
if (loader.isLoading) | |
return AsyncResult.Loading() | |
if (loader.error) | |
return AsyncResult.Err(loader.error) | |
return AsyncResult.Unset() | |
} | |
/* eslint-disable */ | |
static load<S>(setter: (r: AsyncResult<S>) => unknown, promise: Promise<Result<S>>): Promise<AsyncResult<S>>; | |
static load<S>(setter: (r: AsyncResult<S>) => unknown, promise: Promise<unknown>): Promise<AsyncResult<S>> { | |
/* eslint-enable */ | |
setter(AsyncResult.Loading()) | |
return promise | |
.then(result => { | |
if (result instanceof Result) | |
return AsyncResult.from(result) | |
else | |
return AsyncResult.Ok(result) | |
}) | |
.catch(error => { | |
return AsyncResult.Err(error) | |
}) | |
.then(result => { | |
setter(result) | |
return result | |
}) | |
} | |
constructor(state: State, value?: T, error?: Error) { | |
this.state = state | |
this.value = value | |
this.error = error | |
} | |
unwrap(): T { | |
if (this.state !== State.OK) | |
throw new Error('Trying to unwrap an unloaded async result') | |
return (this as unknown as Ok<T>).value as any | |
} | |
unwrapOrElse<S>(value: S): T | S { | |
if (this.state !== State.OK) | |
return value | |
return this.value as any | |
} | |
unwrapErr() { | |
if (this.state !== State.ERROR) | |
throw new Error('Trying to unwrap unexistent error') | |
return (this as unknown as Err).error | |
} | |
map<S>(fn: (m: T) => S): AsyncResult<S> { | |
if (this.state !== State.OK) | |
return (this as unknown as AsyncResult<S>) | |
return AsyncResult.Ok(fn(this.value as any)) | |
} | |
pick<S extends keyof T>(prop: S): AsyncResult<T[S]> { | |
if (this.state !== State.OK) | |
return (this as unknown as AsyncResult<T[S]>) | |
return AsyncResult.Ok(this.value![prop]) | |
} | |
flatMap<S>(fn: (m: T) => AsyncResult<S>): AsyncResult<S> { | |
if (this.state !== State.OK) | |
return (this as unknown as AsyncResult<S>) | |
return fn(this.value as any) | |
} | |
isUnset(): this is Unset { | |
return this.state === State.UNSET | |
} | |
isLoading(): this is Loading { | |
return this.state === State.LOADING | |
} | |
isOk(): this is Ok<T> { | |
return this.state === State.OK | |
} | |
isErr(): this is Err { | |
return this.state === State.ERROR | |
} | |
fold<S>( | |
unsetFn: (() => S) | undefined, | |
loadingFn: () => S, | |
errorFn: (e: Error) => S, | |
loadedFn: (v: T) => S | |
): S { | |
switch (this.state) { | |
case State.UNSET: return (unsetFn ?? loadingFn)() | |
case State.LOADING: return loadingFn() | |
case State.ERROR: return errorFn(this.error as Error) | |
case State.OK: return loadedFn(this.value as T) | |
} | |
} | |
} | |
class Unset extends AsyncResult<any> { | |
declare value: undefined | |
declare error: undefined | |
constructor() { | |
super(State.UNSET, undefined, undefined) | |
} | |
} | |
class Loading extends AsyncResult<any> { | |
declare value: undefined | |
declare error: undefined | |
constructor() { | |
super(State.LOADING, undefined, undefined) | |
} | |
} | |
class Ok<T> extends AsyncResult<T> { | |
declare value: T | |
declare error: undefined | |
constructor(value: T) { | |
super(State.OK, value, undefined) | |
} | |
} | |
class Err extends AsyncResult<any> { | |
declare value: undefined | |
declare error: Error | |
constructor(error: Error) { | |
super(State.ERROR, undefined, error) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment