Repository: gcanti/fp-ts
Pull request #816 (2019-04-05 10:06:43)

New Feature: add parseJSON, stringifyJSON to IOEither


Patch:
diff --git a/docs/modules/IOEither.ts.md b/docs/modules/IOEither.ts.md
index 500806e6..82b09472 100644
--- a/docs/modules/IOEither.ts.md
+++ b/docs/modules/IOEither.ts.md
@@ -32,7 +32,9 @@ error of type `L`. If you want to represent a synchronous computation that never
 - [fromEither (function)](#fromeither-function)
 - [fromLeft (function)](#fromleft-function)
 - [left (function)](#left-function)
+- [parseJSON (function)](#parsejson-function)
 - [right (function)](#right-function)
+- [stringifyJSON (function)](#stringifyjson-function)
 - [~~tryCatch~~ (function)](#trycatch-function)
 - [tryCatch2v (function)](#trycatch2v-function)
 
@@ -211,6 +213,31 @@ export const left = <L, A>(fa: IO<L>): IOEither<L, A> => ...
 
 Added in v1.6.0
 
+# parseJSON (function)
+
+Converts a JavaScript Object Notation (JSON) string into an object.
+
+**Signature**
+
+```ts
+export const parseJSON = <L>(s: string, onError: (reason: unknown) => L): IOEither<L, unknown> => ...
+```
+
+**Example**
+
+```ts
+import { parseJSON } from 'fp-ts/lib/IOEither'
+import { toError } from 'fp-ts/lib/Either'
+
+assert.deepStrictEqual(parseJSON('{"a":1}', toError).run().value, { a: 1 })
+assert.deepStrictEqual(
+  parseJSON('{"a":}', toError).run().value,
+  new SyntaxError('Unexpected token } in JSON at position 5')
+)
+```
+
+Added in v1.16.0
+
 # right (function)
 
 **Signature**
@@ -221,6 +248,33 @@ export const right = <L, A>(fa: IO<A>): IOEither<L, A> => ...
 
 Added in v1.6.0
 
+# stringifyJSON (function)
+
+Converts a JavaScript value to a JavaScript Object Notation (JSON) string.
+
+**Signature**
+
+```ts
+export const stringifyJSON = <L>(u: unknown, onError: (reason: unknown) => L): IOEither<L, string> => ...
+```
+
+**Example**
+
+```ts
+import { stringifyJSON } from 'fp-ts/lib/IOEither'
+import { toError } from 'fp-ts/lib/Either'
+
+assert.deepStrictEqual(stringifyJSON({ a: 1 }, toError).run().value, '{"a":1}')
+const circular: any = { ref: null }
+circular.ref = circular
+assert.deepStrictEqual(
+  stringifyJSON(circular, toError).run().value,
+  new TypeError('Converting circular structure to JSON')
+)
+```
+
+Added in v1.16.0
+
 # ~~tryCatch~~ (function)
 
 Use `tryCatch2v` instead
diff --git a/src/IOEither.ts b/src/IOEither.ts
index 85f7ec0c..81c1de5c 100644
--- a/src/IOEither.ts
+++ b/src/IOEither.ts
@@ -151,6 +151,40 @@ export const tryCatch2v = <L, A>(f: Lazy<A>, onerror: (reason: unknown) => L): I
   return new IOEither(new IO(() => eitherTryCatch2v(f, onerror)))
 }
 
+/**
+ * Converts a JavaScript Object Notation (JSON) string into an object.
+ *
+ * @example
+ * import { parseJSON } from 'fp-ts/lib/IOEither'
+ * import { toError } from 'fp-ts/lib/Either'
+ *
+ * assert.deepStrictEqual(parseJSON('{"a":1}', toError).run().value, { a: 1 })
+ * assert.deepStrictEqual(parseJSON('{"a":}', toError).run().value, new SyntaxError('Unexpected token } in JSON at position 5'))
+ *
+ * @since 1.16.0
+ */
+export const parseJSON = <L>(s: string, onError: (reason: unknown) => L): IOEither<L, unknown> => {
+  return tryCatch2v(() => JSON.parse(s), onError)
+}
+
+/**
+ * Converts a JavaScript value to a JavaScript Object Notation (JSON) string.
+ *
+ * @example
+ * import { stringifyJSON } from 'fp-ts/lib/IOEither'
+ * import { toError } from 'fp-ts/lib/Either'
+ *
+ * assert.deepStrictEqual(stringifyJSON({ a: 1 }, toError).run().value, '{"a":1}')
+ * const circular: any = { ref: null }
+ * circular.ref = circular
+ * assert.deepStrictEqual(stringifyJSON(circular, toError).run().value, new TypeError('Converting circular structure to JSON'))
+ *
+ * @since 1.16.0
+ */
+export const stringifyJSON = <L>(u: unknown, onError: (reason: unknown) => L): IOEither<L, string> => {
+  return tryCatch2v(() => JSON.stringify(u), onError)
+}
+
 const throwError = fromLeft
 
 /**


Test patch:
diff --git a/test/IOEither.ts b/test/IOEither.ts
index db21c593..3e22f4f4 100644
--- a/test/IOEither.ts
+++ b/test/IOEither.ts
@@ -1,7 +1,7 @@
 import * as assert from 'assert'
-import { left as eitherLeft, right as eitherRight } from '../src/Either'
+import { left as eitherLeft, right as eitherRight, toError } from '../src/Either'
 import { IO, io } from '../src/IO'
-import { IOEither, fromLeft, left, right, ioEither, tryCatch } from '../src/IOEither'
+import { IOEither, fromLeft, left, right, ioEither, tryCatch, parseJSON, stringifyJSON } from '../src/IOEither'
 import { none, some } from '../src/Option'
 
 describe('IOEither', () => {
@@ -171,4 +171,28 @@ describe('IOEither', () => {
       assert.deepStrictEqual(ioEither.fromOption(some(1), 'error').run(), eitherRight(1))
     })
   })
+
+  it('parseJSON', () => {
+    assert.deepStrictEqual(parseJSON('{"a":1}', toError).run().value, { a: 1 })
+    assert.deepStrictEqual(
+      parseJSON('{"a":}', toError).run().value,
+      new SyntaxError('Unexpected token } in JSON at position 5')
+    )
+  })
+
+  it('stringifyJSON', () => {
+    assert.deepStrictEqual(stringifyJSON({ a: 1 }, toError).run().value, '{"a":1}')
+    const circular: any = { ref: null }
+    circular.ref = circular
+    assert.deepStrictEqual(
+      stringifyJSON(circular, toError).run().value,
+      new TypeError('Converting circular structure to JSON')
+    )
+    interface Person {
+      name: string
+      age: number
+    }
+    const person: Person = { name: 'Giulio', age: 45 }
+    assert.deepStrictEqual(stringifyJSON(person, toError).run().value, '{"name":"Giulio","age":45}')
+  })
 })