@prismicio/types-internal
Version:
Prismic types for Custom Types and Prismic Data
116 lines (104 loc) • 2.88 kB
text/typescript
import { either } from "fp-ts"
import { pipe } from "fp-ts/function"
import * as t from "io-ts"
import { withValidate } from "io-ts-types"
import { mapOutput } from "io-ts-types/mapOutput"
export function nullable<A, O>(c: t.Type<A, O>) {
return t.union([c, t.null, t.undefined])
}
export function refineType<A, O, I>(
type: t.Type<A, O, I>,
newName: string,
pred: (a: A) => boolean,
): t.Type<A, O, I> {
return new t.Type<A, O, I>(
newName,
type.is,
(u, c) =>
pipe(
type.validate(u, c),
either.chain((v) => (pred(v) ? t.success(v) : t.failure(u, c))),
),
type.encode,
)
}
export function filterDouble(value: string): number | null {
if (value === "") {
return null
}
const result = Number(value)
return Number.isNaN(result) ? null : result
}
export function formatDateTime(date: Date): string {
return date.toISOString().replace(/\.\d{3}Z$/, "+0000")
}
export function formatDate(date: Date): string {
return date.toISOString().replace(/T.*/, "")
}
export function grouped<A>(array: A[], n: number): A[][] {
return array.reduce((acc, curr, idx) => {
if (idx % n === 0) {
acc.push([curr])
} else {
/* eslint-disable @typescript-eslint/no-non-null-assertion */
acc[Math.floor(idx / n)]!.push(curr)
}
return acc
}, new Array<A[]>())
}
export function objectToMap<T>(object: { [key: string]: T }): Map<string, T> {
return new Map(Object.entries(object))
}
export function isEmpty(obj: object) {
for (const _x in obj) {
return false
}
return true
}
export function addType<A, O extends object, I, T extends string>(
codec: t.Type<A, O, I>,
t: T,
): t.Type<A, O & { __TYPE__: T }, I> {
return mapOutput(codec, (o) => ({ ...o, __TYPE__: t } as const))
}
/**
* Returns a clone of the given codec that tries to find sub-error with message already set.
* If there is such error it just returns sub-errors array.
* If there is no such error it generates new error with given message.
*
* @example
* expect(
* withCustomError(
* t.type({age: withCustomError(t.number, () => 'Invalid child')}),
* () => "Invalid parent"
* )
* ).toSubsetEqualLeft([{message: "Invalid child"}])
* expect(
* withCustomError(
* t.type({age: t.number}),
* () => "Invalid parent"
* )
* ).toSubsetEqualLeft([{message: "Invalid parent"}])
*/
export function withCustomError<C extends t.Any>(
codec: C,
message: (i: t.InputOf<C>, c: t.Context) => string,
): C {
return withValidate(codec, (i, c) => {
return either.mapLeft((errors: t.Errors) => {
if (errors.find((error) => error.message)) {
return errors
}
return [
{
value: i,
context: c,
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
message: message(i, c),
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
actual: i,
},
]
})(codec.validate(i, c))
})
}