import { FormikErrors, FormikValues } from 'formik'; export type ErrorMessage = { key: string; message: string; }; /** * Parse Formik errors object into a flat array of error messages * @param errors - Formik errors object * @param parentKey - Parent key for nested objects (used internally for recursion) * @returns Array of error messages */ export function parseFormikErrors( errors: FormikErrors, parentKey: string = '' ): ErrorMessage[] { const errorList: ErrorMessage[] = []; Object.keys(errors).forEach((key) => { const value = errors[key as keyof typeof errors]; const fullKey = parentKey ? `${parentKey}.${key}` : key; if (typeof value === 'string') { // Direct error message errorList.push({ key: fullKey, message: value }); } else if (Array.isArray(value)) { // Array of errors value.forEach((item, index) => { if (typeof item === 'string') { errorList.push({ key: `${fullKey}[${index}]`, message: item }); } else if (item && typeof item === 'object') { // Nested object in array const nestedErrors = parseFormikErrors( item as FormikErrors, `${fullKey}[${index}]` ); errorList.push(...nestedErrors); } }); } else if (value && typeof value === 'object') { // Nested object const nestedErrors = parseFormikErrors( value as FormikErrors, fullKey ); errorList.push(...nestedErrors); } }); return errorList; } /** * Get unique error messages from Formik errors * @param errors - Formik errors object * @returns Array of unique error messages */ export function getUniqueFormikErrors(errors: FormikErrors): string[] { const errorList = parseFormikErrors(errors); return Array.from(new Set(errorList.map((e) => e.message))); } /** * Get all error messages from Formik errors * @param errors - Formik errors object * @returns Array of error messages */ export function getAllFormikErrors(errors: FormikErrors): ErrorMessage[] { return parseFormikErrors(errors); } /** * Check if a value is considered "filled" (not empty) * @param value - Value to check * @returns True if value is filled, false otherwise */ function isValueFilled(value: unknown): boolean { // Check for null or undefined if (value === null || value === undefined) { return false; } // Check for empty string if (typeof value === 'string' && value.trim() === '') { return false; } // Check for empty array if (Array.isArray(value) && value.length === 0) { return false; } // Check for empty object (but not Date or other special objects) if ( typeof value === 'object' && !Array.isArray(value) && !(value instanceof Date) && Object.keys(value).length === 0 ) { return false; } return true; } /** * Count the number of filled (non-empty) values in Formik values object * @param values - Formik values object * @returns Number of filled values * @example * const values = { * name: 'John', * email: '', * age: null, * tags: ['tag1', 'tag2'], * emptyArray: [], * }; * getFilledFormikValuesCount(values); // Returns 2 (name and tags) */ export function getFilledFormikValuesCount( values: T ): number { let count = 0; Object.keys(values).forEach((key) => { const value = values[key]; if (isValueFilled(value)) { count++; } }); return count; }