@docvhcmc/react-form-schema
Version:
A flexible React form schema manager with pluggable validation adapters (Zod, Yup, etc.)
277 lines (270 loc) • 11.5 kB
TypeScript
declare enum ValidationCode {
FIELD_UNKNOWN = "FIELD_UNKNOWN",
UNEXPECTED = "UNEXPECTED"
}
/**
* NonEmptyArray<T>
*
* A tuple-based type that ensures the array contains at least one element.
*
* - The first element is required: [T, ...]
* - Any number of additional elements of type T can follow: [...T[]]
*
* This is useful for enforcing non-empty arrays at the type level,
* such as when you want to guarantee at least one item in a list.
*
* Example:
* const valid: NonEmptyArray<number> = [1, 2, 3]; // ✅ OK
* const invalid: NonEmptyArray<number> = []; // ❌ Error: must have at least one element
*/
type NonEmptyArray<T> = [T, ...T[]];
/**
* Defines the structure of a single field error.
*/
type FieldError = {
/**
* The field path that caused the error (e.g. "email", "user.name").
*/
path: string;
/**
* A machine-readable error code (e.g. "REQUIRED", "INVALID_FORMAT").
*/
code: string;
/**
* An optional default error message. This message can be used as a fallback
* if no translation is found for the `code`, or for simpler cases where i18n
* isn't strictly necessary or for quick debugging. When `code` is provided
* and i18n is used, this message typically serves as a last resort.
*/
message?: string;
origin?: string;
/**
* Additional parameters for i18n or dynamic formatting.
*/
params?: Record<string, unknown>;
};
type SchemaInput<O> = Partial<O>;
type ValidationRule<O> = {
/**
* The path to the field(s) affected by this rule.
* Can be a single field name (string) or an array for nested fields or cross-field validation.
* Examples: 'name', ['address', 'street'], ['password', 'confirmPassword']
*/
path: keyof O | NonEmptyArray<keyof O>;
/**
* A unique error code for this specific rule. This code is **required** and serves as
* the primary key for Internationalization (i18n) lookup. It enables translating
* error messages and programmatically handling specific validation failures.
* Example: 'FORM_AGE_TOO_YOUNG', 'STRING_MIN_LENGTH'.
*/
code: string;
/**
* Optional parameters or context data that can be used to dynamically
* construct the error message. This is essential for parameterized messages
* and Internationalization (i18n).
* Example: `{ min: 5, max: 10 }` for a string length rule, or `{ fieldName: 'Name' }`.
*/
params?: Partial<Record<string, any>>;
/**
* An optional default error message. This message can be used as a fallback
* if no translation is found for the `code`, or for simpler cases where i18n
* isn't strictly necessary or for quick debugging. When `code` is provided
* and i18n is used, this message typically serves as a last resort.
*/
message?: string;
check: (data: O) => boolean | Promise<boolean>;
};
/**
* Interface for the FormErrorManager, responsible for handling validation errors.
*/
interface IFormErrorManager {
/**
* Sets all errors at once, replacing any existing errors.
* @param errors An array of FieldError objects.
*/
setErrors(errors: FieldError[]): void;
/**
* Adds a single error to the current list of errors.
* @param error The FieldError object to add.
*/
addError(error: FieldError): void;
/**
* Checks if any error exists for a specific field path, or if any error exists in general.
* @param path Optional. The dot-notation path of the field (e.g., 'name', 'address.street').
* @returns `true` if an error exists for the specified path or if any error exists, `false` otherwise.
*/
hasError(path?: string): boolean;
/**
* Retrieves the first error message for a specific field.
* @param path The dot-notation path of the field.
* @returns The error message string, or `undefined` if the field has no error.
*/
getFirstFieldError(path: string): string | undefined;
/**
* Retrieves all error objects for a specific field.
* @param path The dot-notation path of the field.
* @returns An array of `FieldError` objects for the specified field.
*/
getErrorsForField(path: string): FieldError[];
/**
* Retrieves all current error objects.
* @returns An array of all `FieldError` objects.
*/
getAllErrors(): FieldError[];
/**
* Clears all existing errors.
*/
clearErrors(): void;
}
/**
* Manages validation errors for a form.
*/
declare class FormErrorManager implements IFormErrorManager {
private _errors;
constructor(initialErrors?: FieldError[]);
/**
* Sets all errors at once, replacing any existing errors.
* @param errors An array of FieldError objects.
*/
setErrors(errors: FieldError[]): void;
/**
* Adds a single error to the current list of errors.
* @param error The FieldError object to add.
*/
addError(error: FieldError): void;
/**
* Checks if any error exists for a specific field path, or if any error exists in general.
* @param path Optional. The dot-notation path of the field (e.g., 'name', 'address.street').
* @returns `true` if an error exists for the specified path or if any error exists, `false` otherwise.
*/
hasError(path?: string): boolean;
/**
* Retrieves the first error message for a specific field.
* @param path The dot-notation path of the field.
* @returns The error message string, or `undefined` if the field has no error.
*/
getFirstFieldError(path: string): string | undefined;
/**
* Retrieves all error objects for a specific field.
* @param path The dot-notation path of the field.
* @returns An array of `FieldError` objects for the specified field.
*/
getErrorsForField(path: string): FieldError[];
/**
* Retrieves all current error objects.
* @returns An array of all `FieldError` objects.
*/
getAllErrors(): FieldError[];
/**
* Clears all existing errors.
*/
clearErrors(): void;
}
/**
* Custom error class for validation failures.
* It provides structured access to field-specific errors.
*/
declare class ValidationError extends Error {
fields: FieldError[];
/**
* Optionally store the original error (e.g., ZodError) for debugging
*/
originalError?: unknown;
/**
* Creates an instance of ValidationError.
* @param fields An array of FieldError objects.
* @param originalError The original error object from the validator (optional).
* @param message A custom error message (optional). If not provided, a default message is generated.
*/
constructor(fields: FieldError[], originalError?: unknown, message?: string);
}
/**
* Interface defining the structure of the object returned by the `useFormSchema` hook.
* @template O - Represents the inferred output type of the schema.
*/
interface UseFormSchemaReturn<O> {
/**
* The current raw (unvalidated and untransformed) input values of the form.
*/
rawInput: SchemaInput<O>;
/**
* Indicates whether the entire form is currently valid (has no validation errors).
*/
isValid: boolean;
/**
* An object providing comprehensive error management utilities.
* It includes methods like `hasError`, `getFirstFieldError`, `getErrorsForField`, `getAllErrors`, and `clearErrors`.
*/
errors: {
/**
* Checks if any error exists for a specific field path, or if any error exists in general.
* @param path Optional. The dot-notation path of the field.
*/
hasError: IFormErrorManager['hasError'];
/**
* Retrieves the first error message for a specific field.
* @param path The dot-notation path of the field.
*/
getFirstFieldError: IFormErrorManager['getFirstFieldError'];
/**
* Retrieves all error objects for a specific field.
* @param path The dot-notation path of the field.
*/
getErrorsForField: IFormErrorManager['getErrorsForField'];
/**
* Retrieves all current error objects.
*/
getAllErrors: IFormErrorManager['getAllErrors'];
/**
* Clears all existing errors.
*/
clearErrors: IFormErrorManager['clearErrors'];
};
/**
* A higher-order function that returns an `onSubmit` handler for HTML forms.
* It performs full form validation and calls either `onSubmitValid` or `onSubmitInvalid` based on the validation result.
* @param onSubmitValid A callback function executed when the form is valid and submitted. Receives the parsed and validated data.
* @param onSubmitInvalid An optional callback function executed when the form is invalid. Receives an array of `FieldError` objects.
* @returns An event handler function suitable for the `onSubmit` prop of a `<form>` element.
*/
handleSubmit: (onSubmitValid: (data: O) => void, onSubmitInvalid?: (errors: FieldError[]) => void) => (event: React.FormEvent) => void;
/**
* Resets the form's internal raw input state to its initial state or an empty state.
* This clears current values and errors.
* @param initialValues Optional. New initial values to reset the form to. If not provided, resets to empty.
*/
reset: (initialValues?: SchemaInput<O>) => void;
/**
* Returns a memoized setter function for a specific form field.
* This setter updates the form's internal raw input state and triggers field-specific validation.
* @param field The name of the field for which to create a setter.
* @returns A function that takes a value (of Input type) and updates the corresponding field.
*/
setValueFor: <F extends keyof O>(field: F) => (value: SchemaInput<O>[F]) => void;
/**
* Overwrites the entire raw input state of the form with the new values.
* Triggers a full form validation.
* @param newValues The new raw input values to set.
*/
/**
* Overwrites the entire raw input state of the form with the new values.
* Triggers a full form validation.
* @param newValues The new raw input values to set.
*/
setRawValue: (newValues: SchemaInput<O>) => void;
/**
* Merges the provided partial values into the existing raw input state.
* Existing fields not present in `partialValues` will be retained. Triggers a full form validation.
* @param partialValues The partial raw input values to merge.
*/
mergeRawValue: (partialValues: SchemaInput<O>) => void;
/**
* Manually triggers validation for the entire form or a specific field.
* Updates the internal error state and notifies subscribers.
* @param field Optional. The name of the field to validate, or a partial input object for external validation.
* @param value Optional. The value for a specific field when `field` is a field name.
* @returns A Promise that resolves to `undefined` if validation passes, or an `Error` instance if validation fails.
*/
validate: (field?: keyof O | SchemaInput<O>, value?: SchemaInput<O>[keyof O]) => Promise<undefined | Error>;
}
export { FormErrorManager as F, type SchemaInput as S, type UseFormSchemaReturn as U, type ValidationRule as V, ValidationError as a, type FieldError as b, ValidationCode as c };