From a6ed36c481110d50bf942b61a87a473c98061788 Mon Sep 17 00:00:00 2001 From: gcanti Date: Tue, 9 Feb 2021 14:12:16 +0100 Subject: [PATCH] add `JsonFromString`, closes #153 --- CHANGELOG.md | 5 ++ docs/modules/JsonFromString.ts.md | 64 ++++++++++++++++++++++++ docs/modules/NonEmptyString.ts.md | 2 +- docs/modules/NumberFromString.ts.md | 2 +- docs/modules/UUID.ts.md | 2 +- docs/modules/mapOutput.ts.md | 2 +- docs/modules/nonEmptyArray.ts.md | 2 +- docs/modules/option.ts.md | 2 +- docs/modules/optionFromNullable.ts.md | 2 +- docs/modules/readonlyNonEmptyArray.ts.md | 2 +- docs/modules/readonlySetFromArray.ts.md | 2 +- docs/modules/regexp.ts.md | 2 +- docs/modules/setFromArray.ts.md | 2 +- docs/modules/withEncode.ts.md | 2 +- docs/modules/withFallback.ts.md | 2 +- docs/modules/withMessage.ts.md | 2 +- docs/modules/withValidate.ts.md | 2 +- package.json | 2 +- src/JsonFromString.ts | 43 ++++++++++++++++ src/index.ts | 5 ++ test/JsonFromString.ts | 36 +++++++++++++ 21 files changed, 169 insertions(+), 16 deletions(-) create mode 100644 docs/modules/JsonFromString.ts.md create mode 100644 src/JsonFromString.ts create mode 100644 test/JsonFromString.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 561b24b..0c43e9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,11 @@ **Note**: Gaps between patch versions are faulty/broken releases. **Note**: A feature tagged as Experimental is in a high state of flux, you're at risk of it changing without notice. +# 0.5.14 + +- **New Feature** + - add `JsonFromString`, closes #153 (@gcanti) + # 0.5.13 - **New Feature** diff --git a/docs/modules/JsonFromString.ts.md b/docs/modules/JsonFromString.ts.md new file mode 100644 index 0000000..c5d16fa --- /dev/null +++ b/docs/modules/JsonFromString.ts.md @@ -0,0 +1,64 @@ +--- +title: JsonFromString.ts +nav_order: 16 +parent: Modules +--- + +# JsonFromString overview + +Added in v0.5.14 + +--- + +

Table of contents

+ +- [JsonArray (interface)](#jsonarray-interface) +- [JsonRecord (interface)](#jsonrecord-interface) +- [Json (type alias)](#json-type-alias) +- [JSONFromString](#jsonfromstring) + +--- + +# JsonArray (interface) + +**Signature** + +```ts +export interface JsonArray extends ReadonlyArray {} +``` + +Added in v0.5.14 + +# JsonRecord (interface) + +**Signature** + +```ts +export interface JsonRecord { + readonly [key: string]: Json +} +``` + +Added in v0.5.14 + +# Json (type alias) + +Copied from `fp-ts/Either` module. + +**Signature** + +```ts +export type Json = boolean | number | string | null | JsonArray | JsonRecord +``` + +Added in v0.5.14 + +# JSONFromString + +**Signature** + +```ts +export const JSONFromString: t.Type = ... +``` + +Added in v0.5.14 diff --git a/docs/modules/NonEmptyString.ts.md b/docs/modules/NonEmptyString.ts.md index 220264e..adb2c0d 100644 --- a/docs/modules/NonEmptyString.ts.md +++ b/docs/modules/NonEmptyString.ts.md @@ -1,6 +1,6 @@ --- title: NonEmptyString.ts -nav_order: 18 +nav_order: 19 parent: Modules --- diff --git a/docs/modules/NumberFromString.ts.md b/docs/modules/NumberFromString.ts.md index 6817eb6..808479f 100644 --- a/docs/modules/NumberFromString.ts.md +++ b/docs/modules/NumberFromString.ts.md @@ -1,6 +1,6 @@ --- title: NumberFromString.ts -nav_order: 19 +nav_order: 20 parent: Modules --- diff --git a/docs/modules/UUID.ts.md b/docs/modules/UUID.ts.md index e461652..d26af6f 100644 --- a/docs/modules/UUID.ts.md +++ b/docs/modules/UUID.ts.md @@ -1,6 +1,6 @@ --- title: UUID.ts -nav_order: 26 +nav_order: 27 parent: Modules --- diff --git a/docs/modules/mapOutput.ts.md b/docs/modules/mapOutput.ts.md index 625871b..98eaf42 100644 --- a/docs/modules/mapOutput.ts.md +++ b/docs/modules/mapOutput.ts.md @@ -1,6 +1,6 @@ --- title: mapOutput.ts -nav_order: 16 +nav_order: 17 parent: Modules --- diff --git a/docs/modules/nonEmptyArray.ts.md b/docs/modules/nonEmptyArray.ts.md index ee11d29..a393e39 100644 --- a/docs/modules/nonEmptyArray.ts.md +++ b/docs/modules/nonEmptyArray.ts.md @@ -1,6 +1,6 @@ --- title: nonEmptyArray.ts -nav_order: 17 +nav_order: 18 parent: Modules --- diff --git a/docs/modules/option.ts.md b/docs/modules/option.ts.md index 32ad9fd..b1dbac5 100644 --- a/docs/modules/option.ts.md +++ b/docs/modules/option.ts.md @@ -1,6 +1,6 @@ --- title: option.ts -nav_order: 20 +nav_order: 21 parent: Modules --- diff --git a/docs/modules/optionFromNullable.ts.md b/docs/modules/optionFromNullable.ts.md index 134be12..8dc3728 100644 --- a/docs/modules/optionFromNullable.ts.md +++ b/docs/modules/optionFromNullable.ts.md @@ -1,6 +1,6 @@ --- title: optionFromNullable.ts -nav_order: 21 +nav_order: 22 parent: Modules --- diff --git a/docs/modules/readonlyNonEmptyArray.ts.md b/docs/modules/readonlyNonEmptyArray.ts.md index a441160..fc2043f 100644 --- a/docs/modules/readonlyNonEmptyArray.ts.md +++ b/docs/modules/readonlyNonEmptyArray.ts.md @@ -1,6 +1,6 @@ --- title: readonlyNonEmptyArray.ts -nav_order: 22 +nav_order: 23 parent: Modules --- diff --git a/docs/modules/readonlySetFromArray.ts.md b/docs/modules/readonlySetFromArray.ts.md index 8291f55..b8da047 100644 --- a/docs/modules/readonlySetFromArray.ts.md +++ b/docs/modules/readonlySetFromArray.ts.md @@ -1,6 +1,6 @@ --- title: readonlySetFromArray.ts -nav_order: 23 +nav_order: 24 parent: Modules --- diff --git a/docs/modules/regexp.ts.md b/docs/modules/regexp.ts.md index aca4e97..7611cfa 100644 --- a/docs/modules/regexp.ts.md +++ b/docs/modules/regexp.ts.md @@ -1,6 +1,6 @@ --- title: regexp.ts -nav_order: 24 +nav_order: 25 parent: Modules --- diff --git a/docs/modules/setFromArray.ts.md b/docs/modules/setFromArray.ts.md index c47b991..831c088 100644 --- a/docs/modules/setFromArray.ts.md +++ b/docs/modules/setFromArray.ts.md @@ -1,6 +1,6 @@ --- title: setFromArray.ts -nav_order: 25 +nav_order: 26 parent: Modules --- diff --git a/docs/modules/withEncode.ts.md b/docs/modules/withEncode.ts.md index b184db8..438d697 100644 --- a/docs/modules/withEncode.ts.md +++ b/docs/modules/withEncode.ts.md @@ -1,6 +1,6 @@ --- title: withEncode.ts -nav_order: 27 +nav_order: 28 parent: Modules --- diff --git a/docs/modules/withFallback.ts.md b/docs/modules/withFallback.ts.md index 1375f4a..ae09957 100644 --- a/docs/modules/withFallback.ts.md +++ b/docs/modules/withFallback.ts.md @@ -1,6 +1,6 @@ --- title: withFallback.ts -nav_order: 28 +nav_order: 29 parent: Modules --- diff --git a/docs/modules/withMessage.ts.md b/docs/modules/withMessage.ts.md index 1d397ad..257c99e 100644 --- a/docs/modules/withMessage.ts.md +++ b/docs/modules/withMessage.ts.md @@ -1,6 +1,6 @@ --- title: withMessage.ts -nav_order: 29 +nav_order: 30 parent: Modules --- diff --git a/docs/modules/withValidate.ts.md b/docs/modules/withValidate.ts.md index d9f0230..e060f0d 100644 --- a/docs/modules/withValidate.ts.md +++ b/docs/modules/withValidate.ts.md @@ -1,6 +1,6 @@ --- title: withValidate.ts -nav_order: 30 +nav_order: 31 parent: Modules --- diff --git a/package.json b/package.json index c415f0c..9514f76 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "io-ts-types", - "version": "0.5.13", + "version": "0.5.14", "description": "A collection of codecs and combinators for use with io-ts", "main": "lib/index.js", "module": "es6/index.js", diff --git a/src/JsonFromString.ts b/src/JsonFromString.ts new file mode 100644 index 0000000..fc6d66d --- /dev/null +++ b/src/JsonFromString.ts @@ -0,0 +1,43 @@ +/** + * @since 0.5.14 + */ +import * as t from 'io-ts' + +/** + * Copied from `fp-ts/Either` module. + * + * @since 0.5.14 + */ +export type Json = boolean | number | string | null | JsonArray | JsonRecord + +/** + * @since 0.5.14 + */ +export interface JsonRecord { + readonly [key: string]: Json +} + +/** + * @since 0.5.14 + */ +export interface JsonArray extends ReadonlyArray {} + +const JsonArray: t.Type = t.recursion('JsonArray', () => t.readonlyArray(Json)) +const JsonRecord: t.Type = t.recursion('JsonRecord', () => t.record(t.string, Json)) +const Json: t.Type = t.union([t.boolean, t.number, t.string, t.null, JsonArray, JsonRecord], 'Json') + +/** + * @since 0.5.14 + */ +export const JSONFromString = new t.Type( + 'JSONFromString', + Json.is, + (s, c) => { + try { + return t.success(JSON.parse(s)) + } catch (e) { + return t.failure(s, c) + } + }, + json => JSON.stringify(json) +) diff --git a/src/index.ts b/src/index.ts index 751e0c0..35cb43c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -123,6 +123,11 @@ export * from './setFromArray' */ export * from './IntFromString' +/** + * @since 0.5.14 + */ +export * from './JsonFromString' + /** * @since 0.5.8 */ diff --git a/test/JsonFromString.ts b/test/JsonFromString.ts new file mode 100644 index 0000000..c9978af --- /dev/null +++ b/test/JsonFromString.ts @@ -0,0 +1,36 @@ +import * as assert from 'assert' +import { JSONFromString } from '../src' +import { assertFailure, assertSuccess } from './helpers' + +describe('JSONFromString', () => { + it('is', () => { + assert.deepStrictEqual(JSONFromString.is(null), true) + assert.deepStrictEqual(JSONFromString.is('a'), true) + assert.deepStrictEqual(JSONFromString.is(1), true) + assert.deepStrictEqual(JSONFromString.is(true), true) + assert.deepStrictEqual(JSONFromString.is(false), true) + assert.deepStrictEqual(JSONFromString.is([]), true) + assert.deepStrictEqual(JSONFromString.is([1]), true) + assert.deepStrictEqual(JSONFromString.is({}), true) + assert.deepStrictEqual(JSONFromString.is({ a: 1 }), true) + assert.deepStrictEqual(JSONFromString.is({ a: Date }), false) + }) + + it('decode', () => { + const T = JSONFromString + assertSuccess(T.decode('null'), null) + assertSuccess(T.decode('1'), 1) + assertSuccess(T.decode('"a"'), 'a') + assertSuccess(T.decode('true'), true) + assertSuccess(T.decode('false'), false) + assertSuccess(T.decode('[]'), []) + assertSuccess(T.decode('{}'), {}) + assertFailure(T, '{', ['Invalid value "{" supplied to : JSONFromString']) + assertFailure(T, '{"a":undefined}', ['Invalid value "{\\"a\\":undefined}" supplied to : JSONFromString']) + }) + + it('encode', () => { + const T = JSONFromString + assert.deepEqual(T.encode({}), '{}') + }) +})