Last active
February 5, 2019 03:48
-
-
Save joshburgess/462c35963c8a6166f369e05325c309d7 to your computer and use it in GitHub Desktop.
io-ts Option encoding/decoding
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as t from 'io-ts' | |
import { Option, None, Some, none, fromEither } from 'fp-ts/lib/Option' | |
export type JSONNone = { _tag: 'None' } | |
export type JSONSome<A> = { _tag: 'Some'; value: A } | |
export type JSONOption<A> = JSONNone | JSONSome<A> | |
export const jsonNone: JSONOption<never> = { _tag: 'None' } | |
export const jsonSome = <A>(value: A): JSONOption<A> => { | |
return { _tag: 'Some', value } | |
} | |
export const isJSONNone = <A>(x: JSONOption<A>): x is JSONNone => | |
x._tag === 'None' | |
export const isJSONSome = <A>(x: JSONOption<A>): x is JSONSome<A> => | |
x._tag === 'Some' | |
export const fold = <A, R>( | |
fa: JSONOption<A>, | |
onNone: R, | |
onSome: (value0: A) => R, | |
): R => (isJSONNone(fa) ? onNone : onSome(fa.value)) | |
export const createOptionFromJSON = <C extends t.Mixed>( | |
codec: C, | |
name: string = `Option<${codec.name}>`, | |
): t.Type<Option<t.TypeOf<C>>, JSONOption<t.OutputOf<C>>, t.mixed> => { | |
const JSONOption_ = t.taggedUnion('_tag', [ | |
t.type({ _tag: t.literal('None') }), | |
t.type({ _tag: t.literal('Some'), value: codec }), | |
]) | |
return new t.Type<Option<t.TypeOf<C>>, JSONOption<t.OutputOf<C>>, t.mixed>( | |
name, | |
(u): u is Option<t.TypeOf<C>> => | |
u instanceof None || (u instanceof Some && codec.is(u.value)), | |
(u, c) => | |
JSONOption_.validate(u, c).chain(o => | |
t.success(fold(o, none, a => fromEither(codec.decode(a)))), | |
), | |
o => o.fold(jsonNone, a => jsonSome(codec.encode(a))), | |
) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as t from 'io-ts' | |
import { Either, right } from 'fp-ts/lib/Either' | |
import { Option, some, none } from 'fp-ts/lib/Option' | |
import { createOptionFromJSON } from './io-ts-option-encoding-decoding' | |
type RoundTripTest<A> = { | |
result: Either<t.Errors, Option<A>> | |
expected: Either<t.Errors, Option<A>> | |
} | |
const roundTrip = <A>(codec: t.Type<A>) => { | |
const OptionFromJSON = createOptionFromJSON(codec) | |
return (ma: Option<A>): RoundTripTest<A> => { | |
const stringified = JSON.stringify(ma) | |
const parsed = JSON.parse(stringified) | |
const result = OptionFromJSON.decode(parsed) | |
const expected = right<t.Errors, Option<A>>(ma) | |
return { result, expected } | |
} | |
} | |
const roundTripTest = roundTrip(t.number) | |
describe('createOptionFromJSON', () => { | |
it('properly encodes and decodes Some', () => { | |
const numOpt = some(1) | |
const { result, expected } = roundTripTest(numOpt) | |
expect(result).toEqual(expected) | |
}) | |
it('properly encodes and decodes None', () => { | |
const { result, expected } = roundTripTest(none) | |
expect(result).toEqual(expected) | |
}) | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment