diff --git a/reports/api-extractor.md b/reports/api-extractor.md index ed30b418e61..bdeb498a821 100644 --- a/reports/api-extractor.md +++ b/reports/api-extractor.md @@ -37,7 +37,7 @@ export type ChangeHandler = (event: { }) => Promise; // @public (undocumented) -export type Control = { +export type Control = { _subjects: Subjects; _removeUnmounted: Noop; _names: Names; @@ -76,7 +76,7 @@ export type Control; register: UseFormRegister; - handleSubmit: UseFormHandleSubmit; + handleSubmit: UseFormHandleSubmit; _disableForm: (disabled?: boolean) => void; unregister: UseFormUnregister; getFieldState: UseFormGetFieldState; @@ -286,10 +286,10 @@ export type FormProps; // @public -export const FormProvider: (props: FormProviderProps) => React_2.JSX.Element; +export const FormProvider: (props: FormProviderProps) => React_2.JSX.Element; // @public (undocumented) -export type FormProviderProps = { +export type FormProviderProps = { children: React_2.ReactNode | React_2.ReactNode[]; } & UseFormReturn; @@ -650,13 +650,13 @@ export type UseFieldArraySwap = (indexA: number, indexB: number) => void; export type UseFieldArrayUpdate = FieldArrayPath> = (index: number, value: FieldArray) => void; // @public -export function useForm(props?: UseFormProps): UseFormReturn; +export function useForm(props?: UseFormProps): UseFormReturn; // @public export type UseFormClearErrors = (name?: FieldPath | FieldPath[] | readonly FieldPath[] | `root.${string}` | 'root') => void; // @public -export const useFormContext: () => UseFormReturn; +export const useFormContext: () => UseFormReturn; // @public export type UseFormGetFieldState = >(name: TFieldName, formState?: FormState) => { @@ -674,7 +674,7 @@ export type UseFormGetValues = { }; // @public -export type UseFormHandleSubmit = (onValid: TTransformedValues extends undefined ? SubmitHandler : TTransformedValues extends FieldValues ? SubmitHandler : never, onInvalid?: SubmitErrorHandler) => (e?: React_2.BaseSyntheticEvent) => Promise; +export type UseFormHandleSubmit = (onValid: SubmitHandler, onInvalid?: SubmitErrorHandler) => (e?: React_2.BaseSyntheticEvent) => Promise; // @public (undocumented) export type UseFormProps = Partial<{ @@ -727,7 +727,7 @@ export type UseFormResetField = ) => void; // @public (undocumented) -export type UseFormReturn = { +export type UseFormReturn = { watch: UseFormWatch; getValues: UseFormGetValues; getFieldState: UseFormGetFieldState; @@ -740,7 +740,7 @@ export type UseFormReturn; handleSubmit: UseFormHandleSubmit; unregister: UseFormUnregister; - control: Control; + control: Control; register: UseFormRegister; setFocus: UseFormSetFocus; }; diff --git a/src/__typetest__/form.test-d.ts b/src/__typetest__/form.test-d.ts index b795c3e2552..0a65b0f5f7e 100644 --- a/src/__typetest__/form.test-d.ts +++ b/src/__typetest__/form.test-d.ts @@ -25,6 +25,23 @@ import { useForm } from '../useForm'; handleSubmit((data) => expectType<{ test: string; test1: number }>(data)); } + + /** it should infer the correct TTransformedValues from useForm generic */ { + /* eslint-disable react-hooks/rules-of-hooks */ + const { handleSubmit } = useForm< + { test: string }, + unknown, + { test: string } | { test1: number } + >(); + + handleSubmit((data) => { + // @ts-expect-error `data` should be union and thus should not be assignable to `{ test: string }` + expectType<{ test: string }>(data); + // @ts-expect-error `data` should be union and thus should not be assignable to `{ test1: number }` + expectType<{ test1: number }>(data); + expectType<{ test: string } | { test1: number }>(data); + }); + } } /** {@link UseFormGetFieldState} */ { diff --git a/src/logic/createFormControl.ts b/src/logic/createFormControl.ts index d4436655c6f..7b8f823ebf0 100644 --- a/src/logic/createFormControl.ts +++ b/src/logic/createFormControl.ts @@ -93,10 +93,14 @@ const defaultOptions = { export function createFormControl< TFieldValues extends FieldValues = FieldValues, TContext = any, + TTransformedValues extends FieldValues = TFieldValues, >( props: UseFormProps = {}, flushRootRender: () => void, -): Omit, 'formState'> { +): Omit< + UseFormReturn, + 'formState' +> { let _options = { ...defaultOptions, ...props, @@ -1096,7 +1100,7 @@ export function createFormControl< } }; - const handleSubmit: UseFormHandleSubmit = + const handleSubmit: UseFormHandleSubmit = (onValid, onInvalid) => async (e) => { let onValidError = undefined; if (e) { @@ -1124,7 +1128,7 @@ export function createFormControl< errors: {}, }); try { - await onValid(fieldValues as TFieldValues, e); + await onValid(fieldValues as TFieldValues & TTransformedValues, e); } catch (error) { onValidError = error; } diff --git a/src/logic/getProxyFormState.ts b/src/logic/getProxyFormState.ts index 2c7402c6e71..07fa95ba6b1 100644 --- a/src/logic/getProxyFormState.ts +++ b/src/logic/getProxyFormState.ts @@ -1,9 +1,13 @@ import { VALIDATION_MODE } from '../constants'; import { Control, FieldValues, FormState, ReadFormState } from '../types'; -export default ( +export default < + TFieldValues extends FieldValues, + TContext = any, + TTransformedValues extends FieldValues = TFieldValues, +>( formState: FormState, - control: Control, + control: Control, localProxyFormState?: ReadFormState, isRoot = true, ) => { diff --git a/src/types/form.ts b/src/types/form.ts index 2f20133ed46..dfba04dbbc8 100644 --- a/src/types/form.ts +++ b/src/types/form.ts @@ -627,13 +627,9 @@ export type UseFormUnregister = ( // eslint-disable-next-line @typescript-eslint/no-unused-vars export type UseFormHandleSubmit< TFieldValues extends FieldValues, - TTransformedValues extends FieldValues | undefined = undefined, + TTransformedValues extends FieldValues = TFieldValues, > = ( - onValid: TTransformedValues extends undefined - ? SubmitHandler - : TTransformedValues extends FieldValues - ? SubmitHandler - : never, + onValid: SubmitHandler, onInvalid?: SubmitErrorHandler, ) => (e?: React.BaseSyntheticEvent) => Promise; @@ -769,6 +765,7 @@ export type BatchFieldArrayUpdate = < export type Control< TFieldValues extends FieldValues = FieldValues, TContext = any, + TTransformedValues extends FieldValues = TFieldValues, > = { _subjects: Subjects; _removeUnmounted: Noop; @@ -815,7 +812,7 @@ export type Control< names: InternalFieldName[], ) => Promise<{ errors: FieldErrors }>; register: UseFormRegister; - handleSubmit: UseFormHandleSubmit; + handleSubmit: UseFormHandleSubmit; _disableForm: (disabled?: boolean) => void; unregister: UseFormUnregister; getFieldState: UseFormGetFieldState; @@ -833,7 +830,7 @@ export type WatchObserver = ( export type UseFormReturn< TFieldValues extends FieldValues = FieldValues, TContext = any, - TTransformedValues extends FieldValues | undefined = undefined, + TTransformedValues extends FieldValues = TFieldValues, > = { watch: UseFormWatch; getValues: UseFormGetValues; @@ -847,7 +844,7 @@ export type UseFormReturn< reset: UseFormReset; handleSubmit: UseFormHandleSubmit; unregister: UseFormUnregister; - control: Control; + control: Control; register: UseFormRegister; setFocus: UseFormSetFocus; }; @@ -879,7 +876,7 @@ export type UseWatchProps = { export type FormProviderProps< TFieldValues extends FieldValues = FieldValues, TContext = any, - TTransformedValues extends FieldValues | undefined = undefined, + TTransformedValues extends FieldValues = TFieldValues, > = { children: React.ReactNode | React.ReactNode[]; } & UseFormReturn; diff --git a/src/useForm.ts b/src/useForm.ts index dc1733cd750..404f2b0cc34 100644 --- a/src/useForm.ts +++ b/src/useForm.ts @@ -46,7 +46,7 @@ import { useSubscribe } from './useSubscribe'; export function useForm< TFieldValues extends FieldValues = FieldValues, TContext = any, - TTransformedValues extends FieldValues | undefined = undefined, + TTransformedValues extends FieldValues = TFieldValues, >( props: UseFormProps = {}, ): UseFormReturn { diff --git a/src/useFormContext.tsx b/src/useFormContext.tsx index 5b54bd6b7b8..6ce43e690da 100644 --- a/src/useFormContext.tsx +++ b/src/useFormContext.tsx @@ -37,7 +37,7 @@ const HookFormContext = React.createContext(null); export const useFormContext = < TFieldValues extends FieldValues, TContext = any, - TransformedValues extends FieldValues | undefined = undefined, + TransformedValues extends FieldValues = TFieldValues, >(): UseFormReturn => React.useContext(HookFormContext) as UseFormReturn< TFieldValues, @@ -78,7 +78,7 @@ export const useFormContext = < export const FormProvider = < TFieldValues extends FieldValues, TContext = any, - TTransformedValues extends FieldValues | undefined = undefined, + TTransformedValues extends FieldValues = TFieldValues, >( props: FormProviderProps, ) => {