Skip to content

Commit 43def95

Browse files
committed
adds optional transform-error callback to recoverCatching to transform caught errors
1 parent 4fb57b5 commit 43def95

File tree

4 files changed

+84
-19
lines changed

4 files changed

+84
-19
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"private": false,
33
"name": "typescript-result",
4-
"version": "3.2.0-beta.5",
4+
"version": "3.2.0-beta.6",
55
"description": "A Result type inspired by Rust and Kotlin that leverages TypeScript's powerful type system to simplify error handling and make your code more readable and maintainable.",
66
"keywords": [
77
"result",

readme.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -793,7 +793,7 @@ const result = Result.all(...tasks.map(createTask)); // Result<Task[], IOError>
793793
- [mapCatching(transformFn, transformErrorFn?)](#mapcatchingtransformfn-transformerrorfn)
794794
- [mapError(transformFn)](#maperrortransformfn)
795795
- [recover(onFailure)](#recoveronfailure)
796-
- [recoverCatching(onFailure)](#recovercatchingonfailure)
796+
- [recoverCatching(onFailure, transformErrorFn?)](#recovercatchingonfailure-transformerrorfn)
797797
- Static methods
798798
- [Result.ok(value)](#resultokvalue)
799799
- [Result.error(error)](#resulterrorerror)
@@ -824,7 +824,7 @@ const result = Result.all(...tasks.map(createTask)); // Result<Task[], IOError>
824824
- [mapCatching(transformFn, transfornErrorFn?)](#mapcatchingtransformfn-transformerrorfn-1)
825825
- [mapError(transformFn)](#maperrortransformfn-1)
826826
- [recover(onFailure)](#recoveronfailure-1)
827-
- [recoverCatching(onFailure)](#recovercatchingonfailure-1)
827+
- [recoverCatching(onFailure, transformErrorFn?)](#recovercatchingonfailure-transformerrorfn-1)
828828

829829
## Result
830830

@@ -1229,7 +1229,7 @@ declare function persistLocally(item: Item): Result<Item, IOError>;
12291229
persistInDB(item).recover(() => persistLocally(item)); // Result<Item, IOError>
12301230
```
12311231

1232-
### recoverCatching(onFailure)
1232+
### recoverCatching(onFailure, transformErrorFn?)
12331233

12341234
Like [`Result.recover`](#recoveronfailure) it transforms a failed result using the `onFailure` callback into a successful result.
12351235
In addition, it catches any exceptions that might be thrown inside the `onFailure` callback and encapsulates them
@@ -1238,6 +1238,7 @@ in a failed result.
12381238
#### Parameters
12391239

12401240
- `onFailure` callback function to transform the error of the result. The callback can be async as well.
1241+
- `transformError` callback function to transform any potential caught error while recovering the result.`
12411242

12421243
**returns** a new successful [`Result`](#result) instance or a new successful [`AsyncResult`](#asyncresult) instance when the result represents a failure, or the original instance if it represents a success.
12431244

@@ -1775,7 +1776,7 @@ declare function persistLocally(item: Item): AsyncResult<Item, IOError>;
17751776
persistInDB(item).recover(() => persistLocally(item)); // AsyncResult<Item, IOError>
17761777
```
17771778

1778-
### recoverCatching(onFailure)
1779+
### recoverCatching(onFailure, transformErrorFn?)
17791780

17801781
Like [`AsyncResult.recover`](#recoveronfailure-1) it transforms a failed result using the `onFailure` callback into a successful result.
17811782
In addition, it catches any exceptions that might be thrown inside the `onFailure` callback and encapsulates them
@@ -1784,6 +1785,7 @@ in a failed result.
17841785
#### Parameters
17851786

17861787
- `onFailure` callback function to transform the error of the result. The callback can be async as well.
1788+
- `transformError` callback function to transform any potential caught error while recovering the result.`
17871789

17881790
**returns** a new successful [`AsyncResult`](#asyncresult) instance when the result represents a failure, or the original instance
17891791
if it represents a success.

src/result.test.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -718,6 +718,26 @@ describe("Result", () => {
718718
Result.assertError(result);
719719
expect(result.error).toBeInstanceOf(CustomError);
720720
});
721+
722+
it("allows you to transform the error that was thrown inside the callback into a new error", async () => {
723+
const asyncResult = Result.fromAsyncCatching(
724+
async (): Promise<number> => {
725+
throw new CustomError("Boom!");
726+
},
727+
(error) => {
728+
expect(error).toBeInstanceOf(CustomError);
729+
return new ErrorA("my message", { cause: error });
730+
},
731+
);
732+
733+
expectTypeOf(asyncResult).toEqualTypeOf<AsyncResult<number, ErrorA>>();
734+
735+
const result = await asyncResult;
736+
737+
Result.assertError(result);
738+
739+
expect(result.error).toBeInstanceOf(ErrorA);
740+
});
721741
});
722742

723743
describe("instance methods and getters", () => {
@@ -1728,6 +1748,27 @@ describe("Result", () => {
17281748
Result.assertOk(await recoveredResult);
17291749
expect(spy).not.toHaveBeenCalled();
17301750
});
1751+
1752+
it("allows you to transform the error that was thrown inside the callback into a new error", () => {
1753+
const result = Result.error(new ErrorA()) as Result<number, ErrorA>;
1754+
1755+
const nextResult = result.recoverCatching(
1756+
(error): number => {
1757+
expectTypeOf(error).toEqualTypeOf<ErrorA>();
1758+
throw new CustomError();
1759+
},
1760+
(error) => {
1761+
expect(error).toBeInstanceOf(CustomError);
1762+
return new ErrorB();
1763+
},
1764+
);
1765+
1766+
expectTypeOf(nextResult).toEqualTypeOf<Result<number, ErrorB>>();
1767+
1768+
Result.assertError(nextResult);
1769+
1770+
expect(nextResult.error).toBeInstanceOf(ErrorB);
1771+
});
17311772
});
17321773
});
17331774

src/result.ts

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -591,29 +591,35 @@ export class AsyncResult<Value, Err> extends Promise<Result<Value, Err>> {
591591
* in a failed result.
592592
*
593593
* @param onFailure callback function to transform the error of the result. The callback can be async as well.
594+
* @param transformError callback function to transform any potential caught error while recovering the result.
594595
* @returns a new successful {@linkcode AsyncResult} instance when the result represents a failure, or the original instance
595596
* if it represents a success.
596597
*/
597598
recoverCatching<
598599
This extends AnyAsyncResult,
599600
ReturnType,
601+
ErrorType = NativeError,
600602
U = Awaited<ReturnType>,
601-
>(this: This, onFailure: (error: InferError<This>) => ReturnType) {
603+
>(
604+
this: This,
605+
onFailure: (error: InferError<This>) => ReturnType,
606+
transformError?: (error: unknown) => ErrorType,
607+
) {
602608
return new AsyncResult<any, any>((resolve, reject) =>
603609
this.then((result) => {
604-
resolve(result.recoverCatching(onFailure));
610+
resolve(result.recoverCatching(onFailure, transformError));
605611
}).catch(reject),
606612
) as [ReturnType] extends [Promise<infer PValue>]
607613
? PValue extends U
608614
? AsyncResult<
609615
InferValue<This> | ExtractValue<U>,
610-
ExtractError<U> | NativeError
616+
ExtractError<U> | ErrorType
611617
>
612618
: never
613619
: ReturnType extends U
614620
? AsyncResult<
615621
InferValue<This> | ExtractValue<U>,
616-
ExtractError<U> | NativeError
622+
ExtractError<U> | ErrorType
617623
>
618624
: never;
619625
}
@@ -1287,38 +1293,48 @@ export class Result<Value, Err> {
12871293
* in a failed result.
12881294
*
12891295
* @param onFailure callback function to transform the error of the result. The callback can be async as well.
1296+
* @param transformError callback function to transform any potential caught error while recovering the result.
12901297
* @returns a new successful {@linkcode Result} instance or a new successful {@linkcode AsyncResult} instance
12911298
* when the result represents a failure, or the original instance if it represents a success.
12921299
*/
1293-
recoverCatching<This extends AnyResult, ReturnType, U = Awaited<ReturnType>>(
1300+
recoverCatching<
1301+
This extends AnyResult,
1302+
ReturnType,
1303+
ErrorType = NativeError,
1304+
U = Awaited<ReturnType>,
1305+
>(
12941306
this: This,
12951307
onFailure: (error: InferError<This>) => ReturnType,
1308+
transformError?: (err: unknown) => ErrorType,
12961309
) {
12971310
return (
12981311
this.success
12991312
? isAsyncFn(onFailure)
13001313
? AsyncResult.ok(this._value)
13011314
: this
1302-
: Result.try(() => onFailure(this._error))
1315+
: Result.try(
1316+
() => onFailure(this._error),
1317+
transformError as AnyFunction,
1318+
)
13031319
) as [ReturnType] extends [Promise<infer PValue>]
13041320
? PValue extends U
13051321
? AsyncResult<
13061322
InferValue<This> | ExtractValue<U>,
1307-
ExtractError<U> | NativeError
1323+
ExtractError<U> | ErrorType
13081324
>
13091325
: never
13101326
: IfReturnsAsync<
13111327
ReturnType,
13121328
ReturnType extends U
13131329
? AsyncResult<
13141330
InferValue<This> | ExtractValue<U>,
1315-
ExtractError<U> | NativeError
1331+
ExtractError<U> | ErrorType
13161332
>
13171333
: never,
13181334
ReturnType extends U
13191335
? Result<
13201336
InferValue<This> | ExtractValue<U>,
1321-
ExtractError<U> | NativeError
1337+
ExtractError<U> | ErrorType
13221338
>
13231339
: never
13241340
>;
@@ -1740,19 +1756,25 @@ export class Result<Value, Err> {
17401756
* Similar to {@linkcode Result.fromAsync} this method transforms an async callback function into an {@linkcode AsyncResult} instance.
17411757
* In addition, it catches any exceptions that might be thrown during the operation and encapsulates them in a failed result.
17421758
*/
1743-
static fromAsyncCatching<T>(
1759+
static fromAsyncCatching<T, ErrorType = NativeError>(
17441760
fn: () => Promise<T>,
1745-
): AsyncResult<ExtractValue<T>, ExtractError<T> | NativeError>;
1761+
transformError?: (err: unknown) => ErrorType,
1762+
): AsyncResult<ExtractValue<T>, ExtractError<T> | ErrorType>;
17461763
/**
17471764
* Similar to {@linkcode Result.fromAsync} this method transforms a Promise into an {@linkcode AsyncResult} instance.
17481765
* In addition, it catches any exceptions that might be thrown during the operation and encapsulates them in a failed result.
17491766
*/
1750-
static fromAsyncCatching<T>(
1767+
static fromAsyncCatching<T, ErrorType = NativeError>(
17511768
value: Promise<T>,
1752-
): AsyncResult<ExtractValue<T>, ExtractError<T> | NativeError>;
1753-
static fromAsyncCatching(valueOrFn: AnyPromise | AnyAsyncFunction) {
1769+
transformError?: (err: unknown) => ErrorType,
1770+
): AsyncResult<ExtractValue<T>, ExtractError<T> | ErrorType>;
1771+
static fromAsyncCatching(
1772+
valueOrFn: AnyPromise | AnyAsyncFunction,
1773+
transformError?: (err: unknown) => any,
1774+
) {
17541775
return Result.try(
17551776
typeof valueOrFn === "function" ? valueOrFn : () => valueOrFn,
1777+
transformError as AnyFunction,
17561778
);
17571779
}
17581780

0 commit comments

Comments
 (0)