Skip to content

Commit

Permalink
Implement AsyncResult.mapErr
Browse files Browse the repository at this point in the history
A piece of AsyncResult API that I find missing every now and then.

A follow-up to [1] and [2].

The purpose, like with the other methods, is to more easily compose
pieces of potentially asynchronous code together.

[1] 3f55d15 ("Take a first stab at async results (#87)")
[2] b5f8af9 ("Implement AsyncResult or and orElse (#109)")
  • Loading branch information
jstasiak committed Mar 4, 2024
1 parent 9a3d33c commit 1bc1349
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 10 deletions.
18 changes: 17 additions & 1 deletion docs/reference/api/asyncresult.rst
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ Or you can use the :ref:`Result.toAsyncResult() <toAsyncResult>` method:
Only the most important methods are currently implemented. The following methods can be
added with not too much effort:
* ``mapErr()``
* ``mapOr()``
* ``mapOrElse()``
Expand Down Expand Up @@ -135,6 +134,23 @@ Example:
await goodResult.map(async (value) => value * 2).promise // Ok(2)
await badResult.andThen(async (value) => value * 2).promise // Err('boo')
``mapErr()``
------------
Maps an ``AsyncResult<T, E>`` to ``AsyncResult<T, F>`` by applying ``mapper`` to the ``Err`` value,
leaving ``Ok`` value untouched.
Example:
.. code-block:: typescript
let goodResult = Ok(1).toAsyncResult()
let badResult = Err('boo').toAsyncResult()
await goodResult.mapErr(async (error) => `Error is ${error}`).promise // Ok(1)
await badResult.mapErr(async (error) => `Error is ${error}`).promise // Err('Error is boo')
``promise``
-----------
Expand Down
30 changes: 21 additions & 9 deletions src/asyncresult.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,18 +80,30 @@ export class AsyncResult<T, E> {
}

// TODO:
// mapErr()
// mapOr()
// mapOrElse()

// mapErr<F>(mapper: (val: E) => F | Promise<F>): AsyncResult<T, F> {
// return this.thenInternal(async (result) => {
// if (result.isOk()) {
// return result
// }
// return Err(await mapper(result.error))
// })
// }
/**
* Maps an `AsyncResult<T, E>` to `AsyncResult<T, F>` by applying `mapper` to the `Err` value,
* leaving `Ok` value untouched.
*
* @example
* ```typescript
* let goodResult = Ok(1).toAsyncResult()
* let badResult = Err('boo').toAsyncResult()
*
* await goodResult.mapErr(async (error) => `Error is ${error}`).promise // Ok(1)
* await badResult.mapErr(async (error) => `Error is ${error}`).promise // Err('Error is boo')
* ```
*/
mapErr<F>(mapper: (val: E) => F | Promise<F>): AsyncResult<T, F> {
return this.thenInternal(async (result) => {
if (result.isOk()) {
return result
}
return Err(await mapper(result.error))
})
}

// async mapOr<U>(default_: U, mapper: (val: T) => U | Promise<U>): Promise<U> {
// return this.mapOrElse(() => default_, mapper)
Expand Down
11 changes: 11 additions & 0 deletions test/asyncresult.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,17 @@ test('map() should work', async () => {
expect(await goodResult.map((value) => Promise.resolve(value * 2)).promise).toEqual(Ok(200))
})

test('mapErr() should work', async () => {
const err = Err('Boo')
const badResult = new AsyncResult(err)
const goodResult = new AsyncResult(Ok(100))

expect(await goodResult.mapErr(_error => {throw new Error('Should not be called')}).promise).toEqual(Ok(100))

expect((await badResult.mapErr(error => `Error is ${error}`).promise).unwrapErr()).toEqual('Error is Boo')
expect((await badResult.mapErr(async error => `Error is ${error}`).promise).unwrapErr()).toEqual('Error is Boo')
})

test('or() should work', async () => {
const err = Err('Boo')
const badResult = new AsyncResult(err)
Expand Down

0 comments on commit 1bc1349

Please sign in to comment.