UNPKG

@abyrd9/zod-form-data

Version:

A Zod-based form data validation library

83 lines (76 loc) 3.78 kB
import { $ZodType } from "zod/v4/core"; import type { DeepPartial } from "./deep-partial"; export type NestedFieldErrors<T extends $ZodType> = T extends { _zod: { def: { type: "object"; shape: infer Shape } } } ? { [K in keyof Shape]: NestedFieldErrors<Shape[K] & $ZodType> } : T extends { _zod: { def: { type: "record"; valueType: infer Value } } } ? Record<string, NestedFieldErrors<Value & $ZodType>> : T extends { _zod: { def: { type: "map"; valueType: infer Value } } } ? Map<string, NestedFieldErrors<Value & $ZodType>> : T extends { _zod: { def: { type: "array"; element: infer Item } } } ? NestedFieldErrors<Item & $ZodType>[] : T extends { _zod: { def: { type: "set"; element: infer Item } } } ? Set<NestedFieldErrors<Item & $ZodType>> : T extends { _zod: { def: { type: "tuple"; items: infer Items } } } ? { [K in keyof Items]: NestedFieldErrors<Items[K] & $ZodType> } : T extends { _zod: { def: { type: "optional"; innerType: infer Inner } } } ? NestedFieldErrors<Inner & $ZodType> : T extends { _zod: { def: { type: "default"; innerType: infer Inner } } } ? NestedFieldErrors<Inner & $ZodType> : T extends { _zod: { def: { type: "nullable"; innerType: infer Inner } } } ? NestedFieldErrors<Inner & $ZodType> : T extends { _zod: { def: { type: "union"; options: infer Options } } } ? Options extends readonly $ZodType[] ? NestedFieldErrors<Options[number] & $ZodType> : string : T extends { _zod: { def: { type: "intersection"; left: infer Left; right: infer Right } } } ? NestedFieldErrors<Left & $ZodType> & NestedFieldErrors<Right & $ZodType> : T extends { _zod: { def: { type: "lazy"; getter: () => infer LazyType } } } ? LazyType extends $ZodType ? NestedFieldErrors<LazyType> : string : T extends { _zod: { def: { type: "transform"; input: infer Input; output: infer Output } } } ? NestedFieldErrors<Input & $ZodType> : T extends { _zod: { def: { type: "pipe"; input: infer Input; output: infer Output } } } ? NestedFieldErrors<Input & $ZodType> : T extends { _zod: { def: { type: "catch"; innerType: infer Inner } } } ? NestedFieldErrors<Inner & $ZodType> : T extends { _zod: { def: { type: "success"; data: infer Data } } } ? string : T extends { _zod: { def: { type: "literal"; value: infer Value } } } ? string : T extends { _zod: { def: { type: "enum"; values: infer Values } } } ? string : T extends { _zod: { def: { type: "promise"; unwrap: infer Unwrapped } } } ? Unwrapped extends $ZodType ? NestedFieldErrors<Unwrapped> : string : T extends { _zod: { def: { type: "custom" } } } ? string : string; export function flattenZodFormErrors<T extends $ZodType>( errors?: DeepPartial<NestedFieldErrors<T>> | null ) { if (!errors) return new Map<string, string>(); const flattenedErrorsMap = new Map<string, string>(); // Flatten object errors function flatten(subErrors: Record<string, unknown>, prefix = "") { for (const [key, value] of Object.entries(subErrors)) { const newPrefix = prefix ? `${prefix}.${key}` : key; if (typeof value === "string") { flattenedErrorsMap.set(newPrefix, value); } else if (Array.isArray(value)) { value.forEach((item, index) => { if (typeof item === "string") { flattenedErrorsMap.set(`${newPrefix}.${index}`, item); } else { flatten(item as Record<string, unknown>, `${newPrefix}.${index}`); } }); } else if (typeof value === "object" && value !== null) { flatten(value as Record<string, unknown>, newPrefix); } } } flatten(errors as unknown as Record<string, unknown>); return flattenedErrorsMap; }