Skip to content

Instantly share code, notes, and snippets.

@therockstorm
Created October 17, 2019 13:32
Show Gist options
  • Save therockstorm/eb597127c72c2c317c8ffe02ea742ad6 to your computer and use it in GitHub Desktop.
Save therockstorm/eb597127c72c2c317c8ffe02ea742ad6 to your computer and use it in GitHub Desktop.
io-ts error reporter
import { pipe } from "fp-ts/lib/pipeable"
import { last } from "fp-ts/lib/Array"
import { Either, left, right, fold as foldE } from "fp-ts/lib/Either"
import { fold as foldO } from "fp-ts/lib/Option"
import {
ContextEntry,
Errors,
getFunctionName,
Type,
Validation,
ValidationError
} from "io-ts"
export interface EmbeddedError {
message: string
path?: string
}
export const decode = <A, O, I>(
type: Type<A, O, I>,
input: I
): Either<EmbeddedError[], A> => {
const res: Either<Errors, A> = type.decode(input)
return pipe(
res,
foldE(() => left(getErrors(res)), a => right(a))
)
}
const getErrors = <A>(v: Validation<A>): EmbeddedError[] => {
return pipe(
v,
foldE(
es =>
es.reduce(
(acc, e) => {
pipe(
last(e.context as ContextEntry[]),
foldO(
() => {},
lc => {
// @ts-ignore
const union = e.context.find(c => c.type._tag === "UnionType")
if (!union) {
if (
// @ts-ignore
!e.context.find(c => c.type._tag === "PartialType")
) {
acc.push(getError(e, lc))
}
} else if (lc.key === "0") acc.push(getError(e, union))
}
)
)
return acc
},
[] as EmbeddedError[]
),
() => []
)
)
}
const getError = (
valErr: ValidationError,
lastCon: ContextEntry
): EmbeddedError => {
const p = valErr.context.filter(c => c.key && c.key !== "0").map(c => c.key)
return valErr.message !== undefined
? { message: valErr.message }
: {
message: `Expected ${lastCon.type.name}, actual ${stringify(
valErr.value
)}`,
path: p.length ? ["", ...p].join("/") : "/"
}
}
const stringify = (v: unknown): string =>
typeof v === "function"
? getFunctionName(v)
: typeof v === "string"
? v
: JSON.stringify(v)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment