Skip to content

Commit

Permalink
Add andThenAsync, orElseAsync
Browse files Browse the repository at this point in the history
  • Loading branch information
bkiac committed Mar 27, 2024
1 parent 0908732 commit d50eaa6
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 0 deletions.
40 changes: 40 additions & 0 deletions src/result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,26 @@ export class ResultImpl<T, E> {
return (this.isOk ? f(this.value as T) : this) as Result<U, E | F>
}

/**
* Calls `f` if the result is `Ok`, otherwise returns `this` (as `Err`).
*
* **Examples**
*
* ```
* let x: Result<number, string> = Ok(2)
* assert.deepStrictEqual(x.andThenAsync((n) => Promise.resolve(Ok(n * 2))), Ok(4))
*
* let y: Result<string, string> = Err("late error")
* assert.deepStrictEqual(y.andThenAsync((n) => Promise.resolve(Ok(n * 2))), Err("late error"))
* ```
*/
andThenAsync<U, F>(
f: (value: T) => ResultPromise<U, F> | Promise<Result<U, F>>,
): ResultPromise<U, E | F> {
const promise = this.isOk ? f(this.value as T) : Promise.resolve(this)
return new ResultPromise<U, E | F>(promise as Promise<Result<U, F>>)
}

/**
* Returns `other` if the result is `Err`, otherwise returns `this` (as `Ok`).
*
Expand Down Expand Up @@ -372,6 +392,26 @@ export class ResultImpl<T, E> {
return (this.isOk ? this : f(this.value as E)) as Result<T | U, F>
}

/**
* Calls `f` if the result is `Err`, otherwise returns `this` (as `Ok`).
*
* **Examples**
*
* ```
* let x: Result<number, string> = Ok(2)
* assert.deepStrictEqual(x.orElseAsync((e) => Promise.resolve(Err(e + "bar"))), Ok(2))
*
* let y: Result<number, string> = Err("foo")
* assert.deepStrictEqual(y.orElseAsync((e) => Promise.resolve(Err(e + "bar"))), Err("foobar"))
* ```
*/
orElseAsync<U, F>(
f: (error: E) => ResultPromise<U, F> | Promise<Result<U, F>>,
): ResultPromise<T | U, F> {
const promise = this.isErr ? f(this.value as E) : Promise.resolve(this)
return new ResultPromise<T | U, F>(promise as Promise<Result<T | U, F>>)
}

/**
* Returns the contained `Ok` value or a provided default.
*
Expand Down
16 changes: 16 additions & 0 deletions src/result_promise.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@ export class ResultPromise<T, E> implements PromiseLike<Result<T, E>> {
return new ResultPromise(this.then((result) => result.andThen((value) => f(value))))
}

/**
* Async version of `Result#andThenAsync`.
*/
andThenAsync<U, F>(f: (value: T) => Promise<Result<U, F>>): ResultPromise<U, E | F> {
return new ResultPromise(this.then((result) => result.andThenAsync((value) => f(value))))
}

/**
* Async version of `Result#expect`.
*/
Expand Down Expand Up @@ -172,6 +179,15 @@ export class ResultPromise<T, E> implements PromiseLike<Result<T, E>> {
return new ResultPromise(this.then((thisResult) => thisResult.orElse((error) => f(error))))
}

/**
* Async version of `Result#orElseAsync`.
*/
orElseAsync<U, F>(f: (error: E) => Promise<Result<U, F>>): ResultPromise<T | U, F> {
return new ResultPromise(
this.then((thisResult) => thisResult.orElseAsync((error) => f(error))),
)
}

/**
* Async version of `Result#unwrap`.
*/
Expand Down
26 changes: 26 additions & 0 deletions test/result.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,18 @@ describe.concurrent("andThen", () => {
})
})

describe.concurrent("andThenAsync", () => {
it("returns the mapped value for an Ok result", async () => {
const a = TestOk<number, string>(0)
await expect(a.andThenAsync(async (value) => Ok(value + 1))).resolves.toEqual(Ok(1))
})

it("returns the result for an Err result", async () => {
const a = TestErr<number, string>("early error")
await expect(a.andThenAsync(async (value) => Ok(value + 1))).resolves.toEqual(a)
})
})

describe.concurrent("expect", () => {
it("returns the value when called on an Ok result", () => {
const result = TestOk<number, string>(42)
Expand Down Expand Up @@ -401,6 +413,20 @@ describe.concurrent("orElse", () => {
it("returns the mapped value for an Err result", () => {
const a = TestErr<number, string>("early error")
expect(a.orElse(() => Ok(1))).toEqual(Ok(1))
expect(a.orElse(() => Err(1))).toEqual(Err(1))
})
})

describe.concurrent("orElseAsync", () => {
it("returns the result for an Ok result", async () => {
const a = TestOk<number, string>(0)
await expect(a.orElseAsync(async () => Ok(1))).resolves.toEqual(a)
})

it("returns the mapped value for an Err result", async () => {
const a = TestErr<number, string>("early error")
await expect(a.orElseAsync(async () => Ok(1))).resolves.toEqual(Ok(1))
await expect(a.orElseAsync(async () => Err(1))).resolves.toEqual(Err(1))
})
})

Expand Down
26 changes: 26 additions & 0 deletions test/result_promise.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,18 @@ describe.concurrent("andThen", () => {
})
})

describe.concurrent("andThenAsync", () => {
it("returns the mapped value for an Ok result", async () => {
const a = TestOkPromise<number, string>(0)
await expect(a.andThenAsync(async (value) => Ok(value + 1))).resolves.toEqual(Ok(1))
})

it("returns the result for an Err result", async () => {
const a = TestErrPromise<number, string>(0)
await expect(a.andThenAsync(async (value) => Ok(value + 1))).resolves.toEqual(Err(0))
})
})

describe.concurrent("expect", () => {
it("returns the value when called on an Ok result", async () => {
const result = new ResultPromise(Promise.resolve(Ok(42)))
Expand Down Expand Up @@ -347,6 +359,20 @@ describe.concurrent("orElse", () => {
it("returns the mapped value for an Err result", () => {
const a = TestErrPromise("error")
expect(a.orElse(() => Ok(1))).toEqual(TestOkPromise(1))
expect(a.orElse(() => Err(1))).toEqual(TestErrPromise(1))
})
})

describe.concurrent("orElseAsync", () => {
it("returns the result for an Ok result", async () => {
const a = TestOkPromise<number, string>(0)
await expect(a.orElseAsync(async () => Ok(1))).resolves.toEqual(Ok(0))
})

it("returns the mapped value for an Err result", async () => {
const a = TestErrPromise<string, string>("original")
await expect(a.orElseAsync(async () => Ok(1))).resolves.toEqual(Ok(1))
await expect(a.orElseAsync(async () => Err(1))).resolves.toEqual(Err(1))
})
})

Expand Down

0 comments on commit d50eaa6

Please sign in to comment.