Skip to content

Commit

Permalink
Fix the AsyncResult.andThen handling of known-Ok transformations (#189)
Browse files Browse the repository at this point in the history
Without these overloads

    goodResult.andThen((value) => Promise.resolve(Ok(value * 2)));

was of type

    AsyncResult<number, unknown>

which would cause downstream problems in the client code.

Granted, this could be an edge case and the map method could be better
for this (Ok -> Ok transformation so only the value inside changes) but
still, I don't think we should be generating unknown here, hence the patch.

Resolves: #176
  • Loading branch information
jstasiak authored Feb 7, 2025
1 parent ce807ef commit 9259f83
Show file tree
Hide file tree
Showing 3 changed files with 14 additions and 2 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ Fixed:

- Fixed `Result.or` and `Result.orElse` method types to actually be callable and return
reasonable types when called.
- Fixed `AsyncResult.andThen` to return the correct type when the provided callback
always returns an `Ok`.

# 4.2.0

Expand Down
4 changes: 4 additions & 0 deletions src/asyncresult.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ export class AsyncResult<T, E> {
* await badResult.andThen(async (value) => Ok(value * 2)).promise // Err('boo')
* ```
*/
andThen<T2>(mapper: (val: T) => Ok<T2> | Promise<Ok<T2>> | AsyncResult<T2, never>): AsyncResult<T2, E>;
andThen<T2, E2>(
mapper: (val: T) => Result<T2, E2> | Promise<Result<T2, E2>> | AsyncResult<T2, E2>,
): AsyncResult<T2, E | E2>;
andThen<T2, E2>(
mapper: (val: T) => Result<T2, E2> | Promise<Result<T2, E2>> | AsyncResult<T2, E2>,
): AsyncResult<T2, E | E2> {
Expand Down
10 changes: 8 additions & 2 deletions test/asyncresult.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { AsyncResult, Err, Ok, Some } from '../src/index.js';
import { AsyncResult, Err, Ok, Result, Some } from '../src/index.js';
import { eq } from './util.js';

test('andThen() should work', async () => {
const err = Err('error');
const badResult = new AsyncResult(err);
const goodResult = new AsyncResult(Ok(100));
const goodResult = new AsyncResult(Ok(100) as Result<number, string>);

expect(
await badResult.andThen(() => {
Expand All @@ -12,6 +13,11 @@ test('andThen() should work', async () => {
).toEqual(err);
expect(await goodResult.andThen((value) => Promise.resolve(Ok(value * 2))).promise).toEqual(Ok(200));
expect(await goodResult.andThen((value) => Ok(value * 3).toAsyncResult()).promise).toEqual(Ok(300));

const afterAndThenOk = goodResult.andThen((value) => Promise.resolve(Ok(value * 2)));
eq<typeof afterAndThenOk, AsyncResult<number, string>>(true);
const afterAndThenResult = goodResult.andThen(() => Ok(true) as Result<boolean, boolean>);
eq<typeof afterAndThenResult, AsyncResult<boolean, string | boolean>>(true);
});

test('map() should work', async () => {
Expand Down

0 comments on commit 9259f83

Please sign in to comment.