diff --git a/.changeset/nine-chefs-sing.md b/.changeset/nine-chefs-sing.md new file mode 100644 index 000000000000..ce4cbd91dc75 --- /dev/null +++ b/.changeset/nine-chefs-sing.md @@ -0,0 +1,9 @@ +--- +"@refinedev/react-hook-form": minor +"@refinedev/core": minor +--- + +feat: improve type parameter specification to useForm +This PR addresses the type parameter issue in the useForm hook. It ensures that the correct types are applied to the form data, resolving type conflicts when handling form submission and validation. + +Resolves [#6137] (https://github.com/refinedev/refine/issues/6137) diff --git a/packages/core/src/hooks/form/index.ts b/packages/core/src/hooks/form/index.ts index 9f09b3663482..ca823b568857 100644 --- a/packages/core/src/hooks/form/index.ts +++ b/packages/core/src/hooks/form/index.ts @@ -59,6 +59,12 @@ export type { * @typeParam TResponseError - Custom error object that extends {@link https://refine.dev/docs/core/interface-references/#httperror `HttpError`}. Defaults to `TError` * */ + +export { + CreateFormVariables, + ExtractFormVariables, + ExtractSubmissionVariables, +} from "./types"; export const useForm = < TQueryFnData extends BaseRecord = BaseRecord, TError extends HttpError = HttpError, diff --git a/packages/core/src/hooks/form/types.ts b/packages/core/src/hooks/form/types.ts index 71f82bf4c6c6..3a43abda7e51 100644 --- a/packages/core/src/hooks/form/types.ts +++ b/packages/core/src/hooks/form/types.ts @@ -29,6 +29,9 @@ import type { LiveModeProps } from "../../contexts/live/types"; import type { SuccessErrorNotification } from "../../contexts/notification/types"; import type { Action } from "../../contexts/router/types"; +const FormVariablesSymbol = Symbol("FormVariables"); +const SubmissionVariablesSymbol = Symbol("SubmissionVariables"); + export type FormAction = Extract; export type RedirectAction = @@ -50,6 +53,22 @@ export type AutoSaveProps = { }; }; +export type CreateFormVariables = { + [FormVariablesSymbol]: TFormVariables; + [SubmissionVariablesSymbol]: TSubmissionVariables; +}; + +export type ExtractFormVariables = T extends { + [FormVariablesSymbol]: infer F; +} + ? F + : T; +export type ExtractSubmissionVariables = T extends { + [SubmissionVariablesSymbol]: infer S; +} + ? S + : T; + export type AutoSaveReturnType< TData extends BaseRecord = BaseRecord, TError extends HttpError = HttpError, diff --git a/packages/react-hook-form/src/useForm/index.ts b/packages/react-hook-form/src/useForm/index.ts index 15163f7ea240..f6d80227e265 100644 --- a/packages/react-hook-form/src/useForm/index.ts +++ b/packages/react-hook-form/src/useForm/index.ts @@ -20,6 +20,9 @@ import { useTranslate, useRefineContext, flattenObjectKeys, + CreateFormVariables, + ExtractFormVariables, + ExtractSubmissionVariables, } from "@refinedev/core"; export type UseFormReturnType< @@ -82,7 +85,7 @@ export type UseFormProps< export const useForm = < TQueryFnData extends BaseRecord = BaseRecord, TError extends HttpError = HttpError, - TVariables extends FieldValues = FieldValues, + TVariables extends FieldValues | CreateFormVariables = FieldValues, TContext extends object = {}, TData extends BaseRecord = TQueryFnData, TResponse extends BaseRecord = TData, @@ -195,6 +198,9 @@ export const useForm = < const { query, onFinish, formLoading, onFinishAutoSave } = useFormCoreResult; + type FormVariablesType = ExtractFormVariables; + type SubmissionVariablesType = ExtractSubmissionVariables; + useEffect(() => { const data = query?.data?.data; if (!data) return; @@ -248,17 +254,24 @@ export const useForm = < return changeValues; }; - const handleSubmit: UseFormHandleSubmit = + const handleSubmit: UseFormHandleSubmit = (onValid, onInvalid) => async (e) => { setWarnWhen(false); - return handleSubmitReactHookForm(onValid, onInvalid)(e); + + const onValidWrapper = (variables: TVariables) => { + const formVariables = + variables as unknown as ExtractFormVariables; + return onValid(formVariables); + }; + + return handleSubmitReactHookForm(onValidWrapper, onInvalid)(e); }; const saveButtonProps = { disabled: formLoading, onClick: (e: React.BaseSyntheticEvent) => { handleSubmit( - (v) => onFinish(v).catch(() => {}), + (v) => onFinish(v as SubmissionVariablesType).catch(() => {}), () => false, )(e); },