Add AsyncDoEither class
This commit is contained in:
parent
07a74315e1
commit
de0160fd9e
134
src/either.ts
134
src/either.ts
@ -176,3 +176,137 @@ export class DoEither<T extends Record<string, unknown>, E> {
|
|||||||
return new DoEither(right({}))
|
return new DoEither(right({}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class for working with Either in a do-notation style with asynchronous computations
|
||||||
|
* @typeParam T - The type of the accumulated scope
|
||||||
|
* @typeParam E - The error type
|
||||||
|
*/
|
||||||
|
export class AsyncDoEither<T extends Record<string, unknown>, E> {
|
||||||
|
/**
|
||||||
|
* @param promise - The current Promise of an Either value
|
||||||
|
*/
|
||||||
|
constructor(private readonly promise: Promise<Either<T, E>>) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds a new value to the scope synchronously
|
||||||
|
* @typeParam K - The key to bind to
|
||||||
|
* @typeParam A - The type of the value to bind
|
||||||
|
* @param key - The property name to bind to
|
||||||
|
* @param fa - The Either value to bind
|
||||||
|
* @returns A new AsyncDoEither instance with the extended scope
|
||||||
|
*/
|
||||||
|
bind<K extends string, A>(
|
||||||
|
key: K,
|
||||||
|
fa: Either<A, E>,
|
||||||
|
): AsyncDoEither<T & Record<K, A>, E> {
|
||||||
|
return this.bindL(key, () => fa)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds a new value to the scope asynchronously
|
||||||
|
* @typeParam K - The key to bind to
|
||||||
|
* @typeParam A - The type of the value to bind
|
||||||
|
* @param key - The property name to bind to
|
||||||
|
* @param f - Function returning Either or Promise of Either
|
||||||
|
* @returns A new AsyncDoEither instance with the extended scope
|
||||||
|
*/
|
||||||
|
bindL<K extends string, A>(
|
||||||
|
key: K,
|
||||||
|
f: (scope: T) => Either<A, E> | Promise<Either<A, E>>,
|
||||||
|
): AsyncDoEither<T & Record<K, A>, E> {
|
||||||
|
const newPromise = this.promise.then(async (currentEither) => {
|
||||||
|
if (isLeft(currentEither)) {
|
||||||
|
return currentEither as unknown as Either<T & Record<K, A>, E>
|
||||||
|
}
|
||||||
|
|
||||||
|
const scope = currentEither.right
|
||||||
|
try {
|
||||||
|
const nextEither = await f(scope)
|
||||||
|
|
||||||
|
if (isLeft(nextEither)) {
|
||||||
|
return nextEither as unknown as Either<T & Record<K, A>, E>
|
||||||
|
}
|
||||||
|
|
||||||
|
return right({
|
||||||
|
...scope,
|
||||||
|
[key]: nextEither.right,
|
||||||
|
}) as Either<T & Record<K, A>, E>
|
||||||
|
} catch (err) {
|
||||||
|
return left(err as E) as Either<T & Record<K, A>, E>
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return new AsyncDoEither(newPromise)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the final result from the scope
|
||||||
|
* @typeParam B - The return type
|
||||||
|
* @param f - The function to transform the scope
|
||||||
|
* @returns A Promise of Either containing the result
|
||||||
|
*/
|
||||||
|
return<B>(f: (scope: T) => B): Promise<Either<B, E>> {
|
||||||
|
return this.promise.then(either => {
|
||||||
|
if (isLeft(either)) {
|
||||||
|
return either as unknown as Either<B, E>
|
||||||
|
}
|
||||||
|
return right(f(either.right))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current scope
|
||||||
|
* @returns A Promise of the current Either scope
|
||||||
|
*/
|
||||||
|
done(): Promise<Either<T, E>> {
|
||||||
|
return this.promise
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes an effect without modifying the scope
|
||||||
|
* @param fa - The Either effect to execute
|
||||||
|
* @returns A new AsyncDoEither instance with the same scope
|
||||||
|
*/
|
||||||
|
do(fa: Either<unknown, E>): AsyncDoEither<T, E> {
|
||||||
|
return this.doL(() => fa)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes an effect that depends on the current scope
|
||||||
|
* @param f - Function returning Either or Promise of Either
|
||||||
|
* @returns A new AsyncDoEither instance with the same scope
|
||||||
|
*/
|
||||||
|
doL(f:
|
||||||
|
(scope: T) =>
|
||||||
|
| Either<unknown, E>
|
||||||
|
| Promise<Either<unknown, E>>): AsyncDoEither<T, E>
|
||||||
|
{
|
||||||
|
const newPromise = this.promise.then(async (currentEither) => {
|
||||||
|
if (isLeft(currentEither)) {
|
||||||
|
return currentEither
|
||||||
|
}
|
||||||
|
|
||||||
|
const scope = currentEither.right
|
||||||
|
try {
|
||||||
|
const effectEither = await f(scope)
|
||||||
|
if (isLeft(effectEither)) {
|
||||||
|
return effectEither as unknown as Either<T, E>
|
||||||
|
}
|
||||||
|
return currentEither
|
||||||
|
} catch (err) {
|
||||||
|
return left(err as E)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return new AsyncDoEither(newPromise)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts a new AsyncDoEither chain with an empty scope
|
||||||
|
* @returns A new AsyncDoEither instance with an empty scope
|
||||||
|
*/
|
||||||
|
static start<E>(): AsyncDoEither<{}, E> {
|
||||||
|
return new AsyncDoEither(Promise.resolve(right({})))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user