Skip to content

Instantly share code, notes, and snippets.

@reverofevil
Created May 3, 2019 22:19
Show Gist options
  • Save reverofevil/c3c1c05e508194a46b5761a8b87088bb to your computer and use it in GitHub Desktop.
Save reverofevil/c3c1c05e508194a46b5761a8b87088bb to your computer and use it in GitHub Desktop.
Isomorphic API typing
import { Type, URIS as Uris } from 'fp-ts/lib/HKT';
type JsonMap = { [K: string]: Json }
interface JsonArray extends Array<Json> {}
type Json = JsonMap | JsonArray | number | string | boolean | null
interface Prim<Uri extends Uris> {
num: Type<Uri, number>;
obj: <O>(ss: { [K in keyof O]: Type<Uri, O[K]> }) => Type<Uri, O>;
}
type Serializer<T> = (t: T) => Json;
type SerializerUri = 'Serializer'
type Deserializer<T> = (j: Json) => T;
type DeserializerUri = 'Deserializer'
declare module 'fp-ts/lib/HKT' {
interface URI2HKT<A> {
Serializer: Serializer<A>;
Deserializer: Deserializer<A>;
}
}
const parser: Prim<DeserializerUri> = {
num(j: Json): number {
if (typeof j !== 'number') {
throw new Error('Number expected');
} else {
return j;
}
},
obj<O>(ss: { [K in keyof O]: Deserializer<O[K]> }) {
return (j: Json): O => {
if (typeof j !== 'object' || Array.isArray(j)) {
throw new Error('Object expected');
}
return Object.keys(ss).reduce((result: O, key: string) => {
return {...result, [key]: (ss as any)[key]((j as any)[key])};
}, {} as O);
};
}
};
const printer: Prim<SerializerUri> = {
num(n: number): Json {
return n;
},
obj<O>(ss: { [K in keyof O]: Serializer<O[K]> }) {
return (o: O): Json => {
return Object.keys(ss).reduce((result: JsonMap, key: string) => {
return {...result, [key]: (ss as any)[key]((o as any)[key])};
}, {} as JsonMap);
};
}
};
const schema = <Uri extends Uris>(p: Prim<Uri>) => p.obj({
a: p.num,
b: p.obj({
c: p.num,
}),
});
const r = schema(parser)(schema(printer)({a: 1, b: {c: 2}}));
console.log(r.b.c);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment