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';