UNPKG

@jumpaku/async-result

Version:

A typescript library that provides Result, Option, and AsyncResult.

266 lines (261 loc) 8.83 kB
import { Result } from "./Result"; export class AsyncResult<V, E> implements PromiseLike<Result<V, E>> { static of<V, E>(result: Result<V, E>): AsyncResult<V, E>; static of<V, E>(result: Promise<Result<V, E>>): AsyncResult<V, unknown>; static of<V, E, F>( result: Promise<Result<V, E>>, catchFun: (error: unknown) => F ): AsyncResult<V, E | F>; static of<V, E, F>( result: Result<V, E> | Promise<Result<V, E>>, catchFun?: (error: unknown) => F ): AsyncResult<V, E | F | unknown> { if (Result.isResult(result)) return new AsyncResult(Promise.resolve(result)); return catchFun == null ? new AsyncResult(result.catch((error: unknown) => Result.failure(error))) : new AsyncResult<V, E | F>( result.catch((error: unknown) => Result.failure(catchFun(error))) ); } static make<V, E>( neverThrowExecutor: ( success: (value: V) => void, failure: (error: E) => void ) => void ): AsyncResult<V, E> { return AsyncResult.of( new Promise<Result<V, E>>((resolve) => neverThrowExecutor( (v: V) => resolve(Result.success(v)), (e: E) => resolve(Result.failure(e)) ) ), (e) => e as E ); } static success<V>(v: V): AsyncResult<V, never> { return AsyncResult.of(Result.success(v)); } static failure<E>(e: E): AsyncResult<never, E> { return AsyncResult.of(Result.failure(e)); } static try<V>(tryFun: () => V | Promise<V>): AsyncResult<V, unknown>; static try<V, E>( tryFun: () => V | Promise<V>, catchFun: (e: unknown) => E ): AsyncResult<V, E>; static try<V, E>( tryFun: () => V | Promise<V>, catchFun?: (e: unknown) => E ): AsyncResult<V, E | unknown> { const promise = new Promise<V>((resolve, reject) => { try { resolve(tryFun()); } catch (error) { reject(error); } }).then(Result.success); return catchFun == null ? AsyncResult.of(promise) : AsyncResult.of(promise, catchFun); } private constructor(readonly promise: Promise<Result<V, E>>) {} then<TResult1 = Result<V, E>, TResult2 = never>( onfulfilled?: | ((value: Result<V, E>) => TResult1 | PromiseLike<TResult1>) | undefined | null, onrejected?: | ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | undefined | null ): Promise<TResult1 | TResult2> { return this.promise.then(onfulfilled, onrejected); } match<X, Y>( onSuccess: (value: V) => X, onFailure: (error: E) => Y ): Promise<X | Y> { return this.promise.then((result) => result.match(onSuccess, onFailure)); } value(): Promise<V | undefined> { return this.promise.then((r) => r.value); } error(): Promise<E | undefined> { return this.promise.then((r) => r.error); } orNull(): Promise<V | null> { return this.promise.then((r) => r.orNull()); } orUndefined(): Promise<V | undefined> { return this.promise.then((r) => r.orUndefined()); } orReject<F>(f?: (e: E) => F): Promise<V> { return this.promise.then((r) => (f == null ? r.orThrow() : r.orThrow(f))); } orDefault(value: V): Promise<V> { return this.promise.then((r) => r.orDefault(value)); } orRecover(neverThrowFun: (e: E) => V): Promise<V> { return this.promise.then((r) => r.orRecover(neverThrowFun)); } onSuccess(neverThrowFun: (value: V) => void): AsyncResult<V, E> { this.promise.then((r) => r.onSuccess(neverThrowFun)); return this; } onFailure(neverThrowFun: (e: E) => void): AsyncResult<V, E> { this.promise.then((r) => r.onFailure(neverThrowFun)); return this; } and<U, F>(other: AsyncResult<U, F>): AsyncResult<V | U, E | F> { return new AsyncResult<V | U, E | F>( this.promise.then((r0) => other.promise.then((r1) => r0.and(r1))) ); } or<U, F>(other: AsyncResult<U, F>): AsyncResult<V | U, E | F> { return new AsyncResult<V | U, E | F>( this.promise.then((r0) => other.promise.then((r1) => r0.or(r1))) ); } map<U>(neverThrowFun: (v: V) => U): AsyncResult<U, E> { return new AsyncResult( this.promise.then((result) => result.map(neverThrowFun)) ); } tryMap<U>(tryFun: (v: V) => U): AsyncResult<U, unknown>; tryMap<U, F>( tryFun: (v: V) => U, catchFun: (error: unknown) => F ): AsyncResult<U, E | F>; tryMap<U, F>( tryFun: (v: V) => U, catchFun?: (error: unknown) => F ): AsyncResult<U, E | F | unknown> { return (catchFun == null ? AsyncResult.try(() => this.promise.then((it) => it.tryMap(tryFun))) : AsyncResult.try( () => this.promise.then((it) => it.tryMap(tryFun, catchFun)), catchFun ) ).flatMap((it) => AsyncResult.of(it)); } flatMap<U, F>( neverThrowFun: (v: V) => AsyncResult<U, F> ): AsyncResult<U, E | F> { const promise = new Promise<Result<U, E | F>>(async (resolve) => { (await this.promise) .onSuccess((value) => resolve(neverThrowFun(value).promise)) .onFailure((error) => resolve(Result.failure(error))); }); return new AsyncResult<U, E | F>(promise); } tryFlatMap<U, F>( tryFun: (v: V) => AsyncResult<U, F> ): AsyncResult<U, unknown>; tryFlatMap<U, F, G>( tryFun: (v: V) => AsyncResult<U, F>, catchFun: (error: unknown) => G ): AsyncResult<U, E | F | G>; tryFlatMap<U, F, G>( tryFun: (v: V) => AsyncResult<U, F>, catchFun?: (error: unknown) => G ): AsyncResult<U, E | F | G | unknown> { if (catchFun == null) { const promise = this.promise .then(async (r) => r.isFailure() ? r.castValue<U>() : tryFun(r.value).promise ) .catch((error) => Result.failure<unknown>(error)); return AsyncResult.of<U, unknown>(promise); } else { const promise = this.promise .then(async (r) => r.isFailure() ? r.castValue<U>() : tryFun(r.value).promise ) .catch((error) => Result.failure<G>(catchFun(error))); return AsyncResult.of<U, E | F | G>(promise); } } recover(neverThrowFun: (e: E) => V): AsyncResult<V, never> { return new AsyncResult<V, never>( this.promise.then((result) => result.recover(neverThrowFun)) ); } tryRecover(tryFun: (error: E) => V): AsyncResult<V, unknown>; tryRecover<F>( tryFun: (error: E) => V, catchFun: (error: unknown) => F ): AsyncResult<V, F>; tryRecover<F>( tryFun: (error: E) => V, catchFun?: (error: unknown) => F ): AsyncResult<V, F | unknown> { return catchFun == null ? AsyncResult.of( this.match(Result.success, (e) => Result.try(() => tryFun(e))) ) : AsyncResult.of( this.match(Result.success, (e) => Result.try(() => tryFun(e), catchFun) ) ); } flatRecover<F>( neverThrowFun: (e: E) => AsyncResult<V, F> ): AsyncResult<V, F> { const promise = new Promise<Result<V, F>>(async (resolve) => { (await this.promise) .onSuccess((value) => resolve(Result.success(value))) .onFailure((error) => resolve(neverThrowFun(error).promise)); }); return new AsyncResult<V, F>(promise); } tryFlatRecover<F>( tryFun: (error: E) => AsyncResult<V, F> ): AsyncResult<V, unknown>; tryFlatRecover<F, G>( tryFun: (error: E) => AsyncResult<V, F>, catchFun: (error: unknown) => G ): AsyncResult<V, F>; tryFlatRecover<F, G>( tryFun: (error: E) => AsyncResult<V, F>, catchFun?: (error: unknown) => G ): AsyncResult<V, F | G | unknown> { const promise = new Promise<Result<V, F | G | unknown>>(async (resolve) => { (await this.promise) .onSuccess((value) => resolve(Result.success(value))) .onFailure((error) => { try { resolve(tryFun(error).promise); } catch (e: unknown) { if (catchFun == null) resolve(Result.failure<unknown>(e)); else resolve(Result.failure<G>(catchFun(e))); } }); }); return new AsyncResult<V, F | G | unknown>(promise); } mapError<F>(neverThrowFun: (error: E) => F): AsyncResult<V, F> { return new AsyncResult( this.promise.then((result) => result.mapError(neverThrowFun)) ); } tryMapError<F>(tryFun: (error: E) => F): AsyncResult<V, unknown>; tryMapError<F, G>( tryFun: (error: E) => F, catchFun: (error: unknown) => G ): AsyncResult<V, F | G>; tryMapError<F, G>( tryFun: (error: E) => F, catchFun?: (error: unknown) => G ): AsyncResult<V, F | G | unknown> { return (catchFun == null ? AsyncResult.try(() => this.promise.then((it) => it.tryMapError(tryFun))) : AsyncResult.try( () => this.promise.then((it) => it.tryMapError(tryFun, catchFun)), catchFun ) ).flatMap((it) => AsyncResult.of(it)); } }