UNPKG

validata

Version:

Type safe data validation and sanitization

73 lines (51 loc) 2.46 kB
export type Contract<T> = { [P in keyof T]-?: ValueProcessor<T[P]>; }; export type Result<T> = ValueResult<T> | IssueResult; export interface ValueResult<T> { value: T; } export const isValue = <T>(result: Result<T> | undefined): result is ValueResult<T> => { return !!result && ('value' in result); }; export const isIssue = <T>(result: Result<T> | undefined): result is IssueResult => { return !!result && (result as IssueResult).issues !== undefined; }; export const exists = <T>(value: T | undefined): value is T => { return value !== undefined; }; export type Path = string | number | symbol; export class Issue { public static forPath = (path: Path | Path[], value: unknown, reason: string, info?: Record<string, unknown>): Issue => { return new Issue(Array.isArray(path) ? path : [path], value, reason, info); }; public static nest = (parentPath: Path | Path[], issue: Issue): Issue => { const path = Array.isArray(parentPath) ? parentPath : [parentPath]; return new Issue([...path, ...issue.path], issue.value, issue.reason, issue.info); }; private constructor( public readonly path: Path[], public readonly value: unknown, public readonly reason: string, public readonly info?: Record<string, unknown>, ) { } } export interface IssueResult { issues: Issue[]; } export interface ValueProcessor<T> { process(value: unknown, path?: Path[]): Result<T>; } export interface AsyncValueProcessor<T> { process(value: unknown, path?: Path[]): Promise<Result<T>>; } export type Next<T, R> = (value: T, path: Path[]) => Result<R>; export interface NotPrimitive { [key: string]: any; } export type KeysOfType<T, U> = { [K in keyof T]: T[K] extends U ? K : never }[keyof T]; export type RequiredKeys<T> = Exclude<KeysOfType<T, Exclude<T[keyof T], undefined>>, undefined>; export type OptionalKeys<T> = Exclude<keyof T, RequiredKeys<T>>; export type OptionalProperties<T extends NotPrimitive> = Pick<T, OptionalKeys<T>>; export type RequiredProperties<T extends NotPrimitive> = Omit<T, OptionalKeys<T>>; export type AllProperties<T extends NotPrimitive> = RequiredProperties<T> & Partial<OptionalProperties<T>>; export type TypeOf<T extends ValueProcessor<V> | Contract<V>, V = unknown> = T extends ValueProcessor<infer Type extends NotPrimitive> ? AllProperties<Type> : { [K in keyof T]: T[K] extends ValueProcessor<infer Type extends NotPrimitive> ? AllProperties<Type> : never };