diff --git a/README.md b/README.md
index 5a939ec..5cca109 100644
--- a/README.md
+++ b/README.md
@@ -143,7 +143,7 @@ withDefault(left(new Error('Wrong!')), 0); // 0
### caseOf
-`(caseof: {Right: (v: A) => B; Left: (v: Error) => any;}, value: Either): Promise`
+`caseOf(caseof: {Right: (v: A) => B; Left: (v: Error) => any;}, value: Either): Promise`
Run different computations depending on whether an `Either` is `Right` or `Left` and returns a `Promise`
@@ -159,7 +159,7 @@ caseOf(
### map
-`(f: (a: A) => B, value: Either): Either`
+`map(f: (a: A) => B, value: Either): Either`
Transforms an `Either` value with a given function.
@@ -169,6 +169,19 @@ map(add1, right(4)); // Right(5)
map(add1, left(new Error('Something bad happened'))); // Left('Something bad happened')
```
+### tryCatch
+
+`tryCatch(f: () => A, onError: (e: Error) => Error): Either`
+
+Transforms a function (that might throw an exception) that produces `A` to a function that produces `Either`.
+
+```ts
+tryCatch(
+ () => JSON.parse(''),
+ err => err
+); // Left('Unexpected end of JSON input')
+```
+
### andThen
`andThen(f: (a: A) => Either, value: Either): Either`
diff --git a/package.json b/package.json
index 5d27eeb..d9efb19 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "ts.data.either",
- "version": "1.0.0",
+ "version": "2.1.0",
"description": "A Typescript implementation of the Either data type",
"main": "dist/index.js",
"types": "dist/index.d.ts",
diff --git a/src/either.spec.ts b/src/either.spec.ts
index 05d865d..3ed042a 100644
--- a/src/either.spec.ts
+++ b/src/either.spec.ts
@@ -7,7 +7,8 @@ import {
map,
andThen,
Either,
- caseOf
+ caseOf,
+ tryCatch
} from './either';
import * as chai from 'chai';
@@ -163,6 +164,32 @@ describe('Either', () => {
});
});
+ describe('tryCatch', () => {
+ let shouldFail: boolean;
+ const fn = () => {
+ if (shouldFail) {
+ throw anError();
+ }
+ return 'Ok';
+ };
+ it('should convert a failing function into a Left', () => {
+ shouldFail = true;
+ return expect(
+ caseOf(
+ {
+ Left: err => `Error: ${err.message}`,
+ Right: n => `Launch ${n} missiles`
+ },
+ tryCatch(fn, err => err)
+ )
+ ).to.be.rejectedWith('Error: Something is wrong');
+ });
+ it('should convert a successful function into a Right', () => {
+ shouldFail = false;
+ expect(tryCatch(fn, err => err)).to.deep.equal(right('Ok'));
+ });
+ });
+
describe('examples', () => {
type Band = {
artist: string;
@@ -237,5 +264,21 @@ describe('Either', () => {
)
).to.be.rejectedWith(`Unexpected end of JSON input`);
});
+
+ it('should be a good tryCatch example', () => {
+ const res = tryCatch(
+ () => JSON.parse(''),
+ err => err
+ );
+ return expect(
+ caseOf(
+ {
+ Left: err => err.message,
+ Right: result => result
+ },
+ res
+ )
+ ).to.be.rejectedWith(`Unexpected end of JSON input`);
+ });
});
});
diff --git a/src/either.ts b/src/either.ts
index f823b11..3d28c97 100644
--- a/src/either.ts
+++ b/src/either.ts
@@ -73,3 +73,14 @@ export const caseOf = (
return Promise.resolve(caseof.Right((value as Right)._value));
}
};
+
+export const tryCatch = (
+ f: () => A,
+ onError: (e: Error) => Error
+): Either => {
+ try {
+ return right(f());
+ } catch (e) {
+ return left(onError(e));
+ }
+};
diff --git a/src/index.ts b/src/index.ts
index 4a1e6eb..bb6a377 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -7,5 +7,6 @@ export {
withDefault,
map,
andThen,
- caseOf
+ caseOf,
+ tryCatch
} from './either';