본문으로 건너뛰기

TypeScript 지원

TypeScript 타입 목차

중요: React Hook Form을 사용하려면 TypeScript 4.1 이상이 필요합니다. 내보내는 여러 타입(재귀적 Path<T> 필드 이름 추론, 템플릿 리터럴 필드 경로)이 이 릴리스에서 도입된 제네릭 기능에 의존하므로 이전 버전에서는 타입 검사가 올바르게 작동하지 않을 수 있습니다. React Hook Form 자체는 TypeScript 6.x를 기준으로 개발하고 테스트합니다.

아래 타입 중 다수는 폼 값 타입(TFieldValues) 외에 두 가지 제네릭 매개변수를 공유합니다.

  • TContext — useForm의 context 옵션에 전달되는 두 번째 인수의 타입입니다. 변경 없이 resolver로 전달되므로 검증 로직에서 폼 외부의 값(예: 로그인한 사용자, 기능 플래그)을 읽을 수 있습니다.
  • TTransformedValues — resolver가 데이터를 변환한 후 생성하는 형태입니다(예: .transform()으로 문자열을 Date로 바꾸는 Zod 스키마). 기본값은 TFieldValues이며, 리졸버가 데이터를 handleSubmit의 onValid 콜백에 전달하기 전에 형태를 변경할 때만 달라집니다.

</> Resolver​

Resolver를 사용하면 React Hook Form의 내장 검증 대신 외부 검증 라이브러리(Zod, Yup, Joi 등)를 연결할 수 있습니다. 현재 폼 값을 받아 { values, errors }를 반환해야 합니다.

import { useForm, Resolver } from "react-hook-form"

type FormValues = {
firstName: string
lastName: string
}

const resolver: Resolver<FormValues> = async (values) => {
return {
values: values.firstName ? values : {},
errors: !values.firstName
? {
firstName: {
type: "required",
message: "This is required.",
},
}
: {},
}
}

export default function App() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm<FormValues>({ resolver })
const onSubmit = handleSubmit((data) => console.log(data))

return (
<form onSubmit={onSubmit}>
<input {...register("firstName")} placeholder="Bill" />
{errors?.firstName && <p>{errors.firstName.message}</p>}

<input {...register("lastName")} placeholder="Luo" />

<input type="submit" />
</form>
)
}

</> SubmitHandler​

import { useForm, SubmitHandler } from "react-hook-form"

type FormValues = {
firstName: string
lastName: string
email: string
}

export default function App() {
const { register, handleSubmit } = useForm<FormValues>()
const onSubmit: SubmitHandler<FormValues> = (data) => console.log(data)

return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register("firstName")} />
<input {...register("lastName")} />
<input type="email" {...register("email")} />

<input type="submit" />
</form>
)
}

</> SubmitErrorHandler​

import { useForm, SubmitHandler, SubmitErrorHandler } from "react-hook-form"

type FormValues = {
firstName: string
lastName: string
email: string
}

export default function App() {
const { register, handleSubmit } = useForm<FormValues>()
const onSubmit: SubmitHandler<FormValues> = (data) => console.log(data)
const onError: SubmitErrorHandler<FormValues> = (errors) =>
console.log(errors)

return (
<form onSubmit={handleSubmit(onSubmit, onError)}>
<input {...register("firstName", { required: true })} />
<input {...register("lastName", { minLength: 2 })} />
<input type="email" {...register("email")} />

<input type="submit" />
</form>
)
}

</> FormSubmitHandler​

Form 컴포넌트의 onSubmit 속성을 위한 타입입니다. SubmitHandler와 달리 페이로드에 제출의 원시 FormData와 formDataJson이 포함됩니다.

export type FormSubmitHandler<TTransformedValues> = (payload: {
data: TTransformedValues
event?: React.BaseSyntheticEvent
formData: FormData
formDataJson: string
method?: "post" | "put" | "delete"
}) => unknown | Promise<unknown>

</> Control​

import { useForm, useWatch, Control } from "react-hook-form"

type FormValues = {
firstName: string
lastName: string
}

function IsolateReRender({ control }: { control: Control<FormValues> }) {
const firstName = useWatch({
control,
name: "firstName",
defaultValue: "default",
})

return <div>{firstName}</div>
}

export default function App() {
const { register, control, handleSubmit } = useForm<FormValues>()
const onSubmit = handleSubmit((data) => console.log(data))

return (
<form onSubmit={onSubmit}>
<input {...register("firstName")} />
<input {...register("lastName")} />
<IsolateReRender control={control} />

<input type="submit" />
</form>
)
}

</> UseFormReturn​

export type UseFormReturn<
TFieldValues extends FieldValues = FieldValues,
TContext = any,
TTransformedValues = TFieldValues,
> = {
watch: UseFormWatch<TFieldValues>
getValues: UseFormGetValues<TFieldValues>
getErrors: UseFormGetErrors<TFieldValues>
getFieldState: UseFormGetFieldState<TFieldValues>
setError: UseFormSetError<TFieldValues>
clearErrors: UseFormClearErrors<TFieldValues>
setValue: UseFormSetValue<TFieldValues>
setValues: UseFormSetValues<TFieldValues>
trigger: UseFormTrigger<TFieldValues>
formState: FormState<TFieldValues>
resetField: UseFormResetField<TFieldValues>
reset: UseFormReset<TFieldValues>
resetDefaultValues: UseFormResetDefaultValues<TFieldValues>
handleSubmit: UseFormHandleSubmit<TFieldValues, TTransformedValues>
unregister: UseFormUnregister<TFieldValues>
control: Control<TFieldValues, TContext, TTransformedValues>
register: UseFormRegister<TFieldValues>
setFocus: UseFormSetFocus<TFieldValues>
subscribe: UseFormSubscribe<TFieldValues>
}

</> UseFormProps​

export type UseFormProps<
TFieldValues extends FieldValues = FieldValues,
TContext = any,
TTransformedValues = TFieldValues,
> = Partial<{
mode: Mode
disabled: boolean
reValidateMode: Exclude<Mode, "onTouched" | "all">
defaultValues: DefaultValues<TFieldValues> | AsyncDefaultValues<TFieldValues>
values: TFieldValues
errors: FieldErrors<TFieldValues>
resetOptions: Parameters<UseFormReset<TFieldValues>>[1]
resolver: Resolver<TFieldValues, TContext, TTransformedValues>
context: TContext
shouldFocusError: boolean
shouldUnregister: boolean
shouldUseNativeValidation: boolean
progressive: boolean
criteriaMode: CriteriaMode
delayError: number
formControl?: Omit<
UseFormReturn<TFieldValues, TContext, TTransformedValues>,
"formState"
>
validate: ValidateForm<TFieldValues>
}>

</> ValidateForm​

useForm의 validate 옵션을 위한 타입입니다. resolver와 함께(또는 대신) 실행되는 폼 수준 검증입니다.

export type ValidateFormEventType =
| "blur"
| "focusout"
| "change"
| "submit"
| "trigger"
| "valid"

export type ValidateForm<TFormValues extends FieldValues> = (props: {
formValues: TFormValues
formState: FormState<TFormValues>
eventType?: ValidateFormEventType
name?: FieldPath<TFormValues> | FieldPath<TFormValues>[]
}) => FormValidateResult<TFormValues> | Promise<FormValidateResult<TFormValues>>

export type FormValidateResult<TFormValues> =
| Partial<
Record<
keyof TFormValues,
{ message: string | string[] | boolean | undefined; type: string }
>
>
| string
| boolean

유효한 폼에는 true를 반환합니다. string을 반환하면 formState.errors.form이 설정되고, 필드 이름을 키로 하는 객체를 반환하면 각 항목에 대해 formState.errors.form.<fieldName>이 설정됩니다.


</> UseFormRegister​

register의 타입입니다.

export type UseFormRegister<TFieldValues extends FieldValues> = <
TFieldName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
>(
name: TFieldName,
options?: RegisterOptions<TFieldValues, TFieldName>
) => UseFormRegisterReturn<TFieldName>

</> UseFormRegisterReturn​

register를 호출할 때 반환되는 형태입니다. 사용자 정의 Input 컴포넌트가 {...register(name)}을 spread할 때 타입을 지정하는 데 유용합니다.

export type UseFormRegisterReturn<
TFieldName extends InternalFieldName = InternalFieldName,
> = {
onChange: ChangeHandler
onBlur: ChangeHandler
ref: RefCallBack
name: TFieldName
min?: string | number
max?: string | number
maxLength?: number
minLength?: number
pattern?: string
required?: boolean
disabled?: boolean
}

</> UseFormUnregister​

unregister의 타입입니다.

export type UseFormUnregister<TFieldValues extends FieldValues> = (
name?:
| FieldPath<TFieldValues>
| FieldPath<TFieldValues>[]
| readonly FieldPath<TFieldValues>[],
options?: Omit<
KeepStateOptions,
| "keepIsSubmitted"
| "keepSubmitCount"
| "keepValues"
| "keepDefaultValues"
| "keepErrors"
> & { keepValue?: boolean; keepDefaultValue?: boolean; keepError?: boolean }
) => void

</> UseFormWatch​

watch의 타입입니다. 오버로드되어 있어 인수 없이, 단일 필드 이름으로, 필드 이름 배열로, 또는 리렌더링 없이 구독하기 위한 콜백으로 호출할 수 있습니다.

export type UseFormWatch<TFieldValues extends FieldValues> = {
(): TFieldValues
<TFieldNames extends readonly FieldPath<TFieldValues>[]>(
names: readonly [...TFieldNames],
defaultValue?: DeepPartial<TFieldValues>
): FieldPathValues<TFieldValues, TFieldNames>
<TFieldName extends FieldPath<TFieldValues>>(
name: TFieldName,
defaultValue?: FieldPathValue<TFieldValues, TFieldName>
): FieldPathValue<TFieldValues, TFieldName>
(
callback: WatchObserver<TFieldValues>,
defaultValues?: DeepPartial<TFieldValues>
): Subscription
}

</> UseFormSubscribe​

subscribe의 타입입니다. 컴포넌트 리렌더링을 실행하지 않고 formState 변경을 구독합니다.

export type UseFormSubscribe<TFieldValues extends FieldValues> = <
TFieldNames extends readonly FieldPath<TFieldValues>[],
>(payload: {
name?: readonly [...TFieldNames] | TFieldNames[number]
formState?: Partial<ReadFormState>
callback: (
data: Partial<FormState<TFieldValues>> & {
values: TFieldValues
name?: InternalFieldName
type?: EventType
}
) => void
exact?: boolean
}) => () => void

</> UseFormHandleSubmit​

handleSubmit 함수의 타입입니다.

export type UseFormHandleSubmit<
TFieldValues extends FieldValues,
TTransformedValues = TFieldValues,
> = (
onValid: SubmitHandler<TTransformedValues>,
onInvalid?: SubmitErrorHandler<TFieldValues>
) => (e?: React.BaseSyntheticEvent) => Promise<void>

</> UseFormReset​

reset 함수의 타입입니다.

export type UseFormReset<TFieldValues extends FieldValues> = (
values?:
| DefaultValues<TFieldValues>
| TFieldValues
| ResetAction<TFieldValues>,
keepStateOptions?: KeepStateOptions
) => void

</> UseFormResetField​

resetField 함수의 타입입니다.

export type UseFormResetField<TFieldValues extends FieldValues> = <
TFieldName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
>(
name: TFieldName,
options?: ResetFieldConfig<TFieldValues, TFieldName>
) => void

</> UseFormResetDefaultValues​

resetDefaultValues 함수의 타입입니다.

export type UseFormResetDefaultValues<TFieldValues extends FieldValues> = (
values: DefaultValues<TFieldValues> | TFieldValues,
options?: Partial<Pick<KeepStateOptions, "keepDirty" | "keepIsValid">>
) => void

</> UseFormSetError​

setError 함수의 타입입니다.

export type UseFormSetError<TFieldValues extends FieldValues> = (
name: FieldPath<TFieldValues> | ErrorNamespacePath,
error: ErrorOption,
options?: {
shouldFocus: boolean
}
) => void

</> UseFormClearErrors​

clearErrors 함수의 타입입니다.

export type UseFormClearErrors<TFieldValues extends FieldValues> = (
name?:
| FieldPath<TFieldValues>
| FieldPath<TFieldValues>[]
| readonly FieldPath<TFieldValues>[]
| ErrorNamespacePath
) => void

</> UseFormSetValue​

setValue 함수의 타입입니다.

export type UseFormSetValue<TFieldValues extends FieldValues> = <
TFieldName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
>(
name: TFieldName,
value: FieldPathValue<TFieldValues, TFieldName>,
options?: SetValueConfig
) => void

</> UseFormSetValues​

setValues 함수의 타입입니다.

export type UseFormSetValues<TFieldValues extends FieldValues> = (
value: Partial<TFieldValues> | ((formValues: TFieldValues) => TFieldValues),
options?: SetValueConfig
) => void

</> UseFormSetFocus​

setFocus 함수의 타입입니다.

export type SetFocusOptions = Partial<{
shouldSelect: boolean
}>

export type UseFormSetFocus<TFieldValues extends FieldValues> = <
TFieldName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
>(
name: TFieldName,
options?: SetFocusOptions
) => void

</> UseFormGetValues​

getValues 함수의 타입입니다. 오버로드되어 있어 인수 없이, 단일 필드 이름으로 또는 필드 이름 배열로 호출할 수 있습니다.

export type GetValuesConfig = {
dirtyFields?: boolean
touchedFields?: boolean
}

export type UseFormGetValues<TFieldValues extends FieldValues> = {
(name?: undefined, config?: GetValuesConfig): TFieldValues
<TFieldName extends FieldPath<TFieldValues>>(
name: TFieldName,
config?: GetValuesConfig
): FieldPathValue<TFieldValues, TFieldName>
<TFieldNames extends FieldPath<TFieldValues>[]>(
names: readonly [...TFieldNames],
config?: GetValuesConfig
): [...FieldPathValues<TFieldValues, TFieldNames>]
}

</> UseFormGetErrors​

getErrors 함수의 타입입니다. 오버로드되어 있어 인수 없이, 단일 오류 경로로 또는 오류 경로 배열로 호출할 수 있습니다.

export type ErrorNamespacePath =
| "root"
| `root.${string}`
| "form"
| `form.${string}`

export type GetErrorsResult<
TFieldValues extends FieldValues,
TName extends FieldPath<TFieldValues> | ErrorNamespacePath,
> = TName extends "root"
? (Record<string, GlobalError> & GlobalError) | undefined
: TName extends ErrorNamespacePath
? GlobalError | undefined
: TName extends FieldPath<TFieldValues>
? FieldPathError<TFieldValues, TName> | undefined
: never

export type UseFormGetErrors<TFieldValues extends FieldValues> = {
(name?: undefined): FieldErrors<TFieldValues>
<TName extends FieldPath<TFieldValues> | ErrorNamespacePath>(
name: TName
): GetErrorsResult<TFieldValues, TName>
<TNames extends (FieldPath<TFieldValues> | ErrorNamespacePath)[]>(
names: readonly [...TNames]
): {
[K in keyof TNames]: GetErrorsResult<TFieldValues, TNames[K]>
}
}

</> UseFormGetFieldState​

getFieldState 함수의 타입입니다.

export type UseFormGetFieldState<TFieldValues extends FieldValues> = <
TFieldName extends FieldPath<TFieldValues>,
>(
name: TFieldName,
formState?: FormState<TFieldValues>
) => {
invalid: boolean
isDirty: boolean
isTouched: boolean
isValidating: boolean
error?: FieldError
}

</> UseFormTrigger​

trigger 함수의 타입입니다.

export type TriggerConfig = Partial<{
shouldFocus: boolean
shouldTouch: boolean
}>

export type UseFormTrigger<TFieldValues extends FieldValues> = (
name?:
| FieldPath<TFieldValues>
| FieldPath<TFieldValues>[]
| readonly FieldPath<TFieldValues>[],
options?: TriggerConfig
) => Promise<boolean>

</> UseWatchProps​

useWatch 훅이 받는 속성입니다.

export type UseWatchProps<TFieldValues extends FieldValues = FieldValues> = {
defaultValue?: unknown
disabled?: boolean
name?:
| FieldPath<TFieldValues>
| FieldPath<TFieldValues>[]
| readonly FieldPath<TFieldValues>[]
control?: Control<TFieldValues>
exact?: boolean
compute?: (formValues: TFieldValues) => TFieldValues
}

</> WatchProps​

Watch 렌더 속성 컴포넌트를 위한 속성입니다.

export type WatchProps<
TFieldName extends WatchName<TFieldValues>,
TFieldValues extends FieldValues = FieldValues,
TContext = any,
TTransformedValues = TFieldValues,
TComputeValue = undefined,
> = {
control?: Control<TFieldValues, TContext, TTransformedValues>
name?: TFieldName
disabled?: boolean
exact?: boolean
defaultValue?: WatchDefaultValue<TFieldName, TFieldValues>
compute?: (value: WatchValue<TFieldName, TFieldValues>) => TComputeValue
render: (
value: WatchRenderValue<TFieldName, TFieldValues, TComputeValue>
) => React.ReactNode | React.ReactNode[]
}

</> UseFormStateProps​

useFormState 훅이 받는 속성입니다.

export type UseFormStateProps<
TFieldValues extends FieldValues,
TTransformedValues = TFieldValues,
> = Partial<{
control?: Control<TFieldValues, any, TTransformedValues>
disabled?: boolean
name?:
| FieldPath<TFieldValues>
| FieldPath<TFieldValues>[]
| readonly FieldPath<TFieldValues>[]
exact?: boolean
}>

</> FormStateProps​

FormState 렌더 속성 컴포넌트를 위한 속성입니다.

export type FormStateProps<
TFieldValues extends FieldValues,
TTransformedValues = TFieldValues,
> = UseFormStateProps<TFieldValues, TTransformedValues> & {
render: (values: UseFormStateReturn<TFieldValues>) => React.ReactNode
}

</> FormProviderProps​

export type FormProviderProps<
TFieldValues extends FieldValues = FieldValues,
TContext = any,
TTransformedValues = TFieldValues,
> = {
children: React.ReactNode | React.ReactNode[]
} & UseFormReturn<TFieldValues, TContext, TTransformedValues>

</> FormProps​

export type FormProps<
TFieldValues extends FieldValues,
TTransformedValues = TFieldValues,
> = Omit<React.FormHTMLAttributes<HTMLFormElement>, "onError" | "onSubmit"> &
Partial<{
control: Control<TFieldValues, any, TTransformedValues>
headers: Record<string, string>
validateStatus: (status: number) => boolean
onError: (
payload:
| { response: Response; error?: undefined }
| { response?: undefined; error: unknown }
) => void
onSuccess: ({ response }: { response: Response }) => void
onSubmit: FormSubmitHandler<TTransformedValues>
method: "post" | "put" | "delete"
children: React.ReactNode | React.ReactNode[]
render: (props: {
submit: (e?: React.FormEvent) => void
}) => React.ReactNode | React.ReactNode[]
encType:
| "application/x-www-form-urlencoded"
| "multipart/form-data"
| "text/plain"
| "application/json"
}>

</> UseFieldArrayReturn​

export type UseFieldArrayReturn<
TFieldValues extends FieldValues = FieldValues,
TFieldArrayName extends
FieldArrayPath<TFieldValues> = FieldArrayPath<TFieldValues>,
TKeyName extends string = "id",
> = {
swap: UseFieldArraySwap
move: UseFieldArrayMove
prepend: UseFieldArrayPrepend<TFieldValues, TFieldArrayName>
append: UseFieldArrayAppend<TFieldValues, TFieldArrayName>
remove: UseFieldArrayRemove
insert: UseFieldArrayInsert<TFieldValues, TFieldArrayName>
update: UseFieldArrayUpdate<TFieldValues, TFieldArrayName>
replace: UseFieldArrayReplace<TFieldValues, TFieldArrayName>
fields: (FieldArrayWithId<TFieldValues, TFieldArrayName, TKeyName> & {
disabled?: boolean
})[]
}

</> FieldArrayMethodProps​

append, prepend, insert 함수의 두 번째 인수로 받는 포커스 옵션입니다.

export type FieldArrayMethodProps = {
shouldFocus?: boolean
focusIndex?: number
focusName?: string
}

</> UseFieldArrayProps​

export type UseFieldArrayProps<
TFieldValues extends FieldValues = FieldValues,
TFieldArrayName extends
FieldArrayPath<TFieldValues> = FieldArrayPath<TFieldValues>,
TKeyName extends string = "id",
TTransformedValues = TFieldValues,
> = {
name: TFieldArrayName
keyName?: TKeyName
control?: Control<TFieldValues, any, TTransformedValues>
rules?: {
validate?:
| Validate<FieldArray<TFieldValues, TFieldArrayName>[], TFieldValues>
| Record<
string,
Validate<FieldArray<TFieldValues, TFieldArrayName>[], TFieldValues>
>
} & Pick<
RegisterOptions<TFieldValues>,
"maxLength" | "minLength" | "required"
>
shouldUnregister?: boolean
disabled?: boolean
}

</> FieldArrayProps​

export type FieldArrayProps<
TFieldValues extends FieldValues = FieldValues,
TFieldArrayName extends
FieldArrayPath<TFieldValues> = FieldArrayPath<TFieldValues>,
TKeyName extends string = "id",
> = {
render: (
fieldArray: UseFieldArrayReturn<TFieldValues, TFieldArrayName, TKeyName>
) => React.ReactElement
} & UseFieldArrayProps<TFieldValues, TFieldArrayName, TKeyName>

</> ControllerRenderProps​

field 객체가 Controller의 render 속성에 전달될 때의 형태입니다. 사용자 정의 제어 입력 컴포넌트의 타입을 지정할 때 유용합니다.

export type ControllerRenderProps<
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
> = {
onChange: (...event: any[]) => void
onBlur: Noop
value: FieldPathValue<TFieldValues, TName>
disabled?: boolean
name: TName
ref: RefCallBack
}

</> ControllerFieldState​

fieldState 객체가 Controller의 render 속성에 전달될 때의 형태입니다.

export type ControllerFieldState = {
invalid: boolean
isTouched: boolean
isDirty: boolean
isValidating: boolean
error?: FieldError
}

</> UseControllerReturn​

export type UseControllerReturn<
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
> = {
field: ControllerRenderProps<TFieldValues, TName>
formState: UseFormStateReturn<TFieldValues>
fieldState: ControllerFieldState
}

</> UseControllerProps​

export type UseControllerProps<
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
TTransformedValues = TFieldValues,
> = {
name: TName
rules?: Omit<
RegisterOptions<TFieldValues, TName>,
"valueAsNumber" | "valueAsDate" | "setValueAs" | "disabled"
>
shouldUnregister?: boolean
defaultValue?: FieldPathValue<TFieldValues, TName>
control?: Control<TFieldValues, any, TTransformedValues>
disabled?: boolean
exact?: boolean
}

</> ControllerProps​

export type ControllerProps<
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
TTransformedValues = TFieldValues,
> = {
render: ({
field,
fieldState,
formState,
}: {
field: ControllerRenderProps<TFieldValues, TName>
fieldState: ControllerFieldState
formState: UseFormStateReturn<TFieldValues>
}) => React.ReactElement
} & UseControllerProps<TFieldValues, TName, TTransformedValues>

</> FieldError​

export type FieldError = {
type: LiteralUnion<keyof RegisterOptions, string>
root?: FieldError
ref?: Ref
types?: MultipleFieldErrors
message?: Message
}

</> FieldErrors​

export type FieldErrors<T extends FieldValues = FieldValues> = Partial<
FieldErrorsImpl<DeepRequired<T>>
> & {
root?: Record<string, GlobalError> & GlobalError
form?: GlobalError
}

</> ErrorOption​

setError가 받는 오류 객체입니다.

export type ErrorOption = {
message?: Message
type?: LiteralUnion<keyof RegisterOptions, string>
types?: MultipleFieldErrors
}

</> Field​

export type Field = {
_f: {
ref: Ref
name: InternalFieldName
refs?: HTMLInputElement[]
mount?: boolean
} & RegisterOptions
}

</> FieldPath​

사용자 정의 컴포넌트의 name 속성을 정의할 때 유용하며 필드 경로를 기준으로 타입을 검사합니다.

export type FieldPath<TFieldValues extends FieldValues> = Path<TFieldValues>

</> FieldPathByValue​

이 타입은 전달된 값과 일치하는 모든 경로의 유니온을 반환합니다.

export type FieldPathByValue<TFieldValues extends FieldValues, TValue> = {
[Key in FieldPath<TFieldValues>]: FieldPathValue<
TFieldValues,
Key
> extends TValue
? Key
: never
}[FieldPath<TFieldValues>]

</> FieldValues​

export type FieldValues = Record<string, any>

</> FieldArrayWithId​

export type FieldArrayWithId<
TFieldValues extends FieldValues = FieldValues,
TFieldArrayName extends
FieldArrayPath<TFieldValues> = FieldArrayPath<TFieldValues>,
TKeyName extends string = "id",
> = FieldArray<TFieldValues, TFieldArrayName> & Record<TKeyName, string>

</> Mode​

export type ValidationMode = typeof VALIDATION_MODE

export type Mode = keyof ValidationMode

</> RegisterOptions​

export type RegisterOptions<
TFieldValues extends FieldValues = FieldValues,
TFieldName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
> = Partial<{
required: Message | ValidationRule<boolean>
min: ValidationRule<number | string>
max: ValidationRule<number | string>
maxLength: ValidationRule<number>
minLength: ValidationRule<number>
validate:
| Validate<FieldPathValue<TFieldValues, TFieldName>, TFieldValues>
| Record<
string,
Validate<FieldPathValue<TFieldValues, TFieldName>, TFieldValues>
>
value: FieldPathValue<TFieldValues, TFieldName>
setValueAs: (value: any) => any
shouldUnregister?: boolean
onChange?: (event: any) => void
onBlur?: (event: any) => void
disabled: boolean
deps: FieldPath<TFieldValues> | FieldPath<TFieldValues>[]
}> &
(
| {
pattern?: ValidationRule<RegExp>
valueAsNumber?: false
valueAsDate?: false
}
| {
pattern?: undefined
valueAsNumber?: false
valueAsDate?: true
}
| {
pattern?: undefined
valueAsNumber?: true
valueAsDate?: false
}
)

</> FormState​

formState의 타입이며 useForm과 useFormState가 반환합니다.

export type FormState<TFieldValues extends FieldValues> = {
isDirty: boolean
isLoading: boolean
isSubmitted: boolean
isSubmitSuccessful: boolean
isSubmitting: boolean
isValidating: boolean
isValid: boolean
disabled: boolean
submitCount: number
defaultValues?: undefined | Readonly<DeepPartial<TFieldValues>>
dirtyFields: Partial<Readonly<FieldNamesMarkedBoolean<TFieldValues>>>
touchedFields: Partial<Readonly<FieldNamesMarkedBoolean<TFieldValues>>>
validatingFields: Partial<Readonly<FieldNamesMarkedBoolean<TFieldValues>>>
errors: FieldErrors<TFieldValues>
isReady: boolean
}

</> KeepStateOptions​

reset과 useForm의 resetOptions가 받는 옵션입니다. 초기화 후에도 유지할 폼 상태 부분을 제어합니다.

export type KeepStateOptions = Partial<{
keepDirtyValues: boolean
keepErrors: boolean
keepDirty: boolean
keepValues: boolean
keepDefaultValues: boolean
keepIsSubmitted: boolean
keepIsSubmitSuccessful: boolean
keepTouched: boolean
keepIsValidating: boolean
keepIsValid: boolean
keepSubmitCount: boolean
keepFieldsRef: boolean
}>

</> FormStateProxy​

export type FormStateProxy<TFieldValues extends FieldValues = FieldValues> = {
isDirty: boolean
isValidating: boolean
dirtyFields: FieldNamesMarkedBoolean<TFieldValues>
touchedFields: FieldNamesMarkedBoolean<TFieldValues>
validatingFields: FieldNamesMarkedBoolean<TFieldValues>
errors: boolean
isValid: boolean
}

</> OpaqueTypes​

재귀 타입 헬퍼(Path, DeepPartial, FieldErrors 등)가 속성 내부로 재귀하는 대신 불투명한 리프 값으로 취급할 타입의 레지스트리입니다. 기본적으로 비어 있으며 선언 병합을 통해 타입을 등록하기 전에는 아무런 효과가 없습니다.

export interface OpaqueTypes {}

export type OpaqueType = OpaqueTypes[keyof OpaqueTypes]

Day.js의 Dayjs, Decimal.js의 Decimal, Luxon의 DateTime 같은 풍부한 서드파티 값 타입은 OpaqueTypes를 .d.ts 파일(또는 컴파일러에 모듈이 보이는 위치)에서 확장해 등록합니다.

import type { Dayjs } from "dayjs"

declare module "react-hook-form" {
interface OpaqueTypes {
dayjs: Dayjs
}
}

등록하면 Dayjs 타입의 필드(또는 구조적으로 이를 포함하는 타입)는 폼 값에서 얼마나 깊게 중첩되어 있든 React Hook Form의 내부 타입 헬퍼에서 리프로 취급됩니다.

장점​

타입을 등록하지 않으면 Path<T>와 DeepPartial<T> 같은 헬퍼가 TFieldValues에 있는 모든 객체의 모든 속성으로 재귀하며, 여기에는 Dayjs나 Decimal 같은 서드파티 클래스 내부도 포함됩니다. 크거나 깊게 중첩된 폼에서는 이 재귀 비용 때문에 TypeScript가 "excessively deep" 복잡도 진단을 출력하거나 편집기의 타입 검사가 느려질 수 있습니다.

OpaqueTypes에 타입을 등록하면 해당 타입에서 멈추고 순회할 객체가 아닌 단일 리프 값으로 취급하도록 헬퍼에 알립니다. 내부에서 이미 Date와 FileList를 처리하는 방식과 같습니다. 따라서 단순한 원시 값 대신 풍부한 객체를 저장하는 폼에서도 필드 경로 추론과 다른 제네릭 타입 계산을 빠르고 정확하게 유지할 수 있습니다.


</> NestedValue (7.33.0에서 지원 중단 예정)​

NestedValue는 지원 중단 예정이며 향후 메이저 버전에서 제거됩니다. 아래 배열 값처럼 제어 상태를 유지해야 하는 원시 값이 아닌 값에는 폼 값에서 필드 타입을 일반적으로 지정하고 Controller에 연결합니다. 타입을 NestedValue로 감싸지 않습니다.

import { useEffect } from "react"
import { useForm, NestedValue } from "react-hook-form"
import { TextField, Select, MenuItem } from "@mui/material"
import { Autocomplete } from "@mui/material"

type Option = {
label: string
value: string
}

const options = [
{ label: "Chocolate", value: "chocolate" },
{ label: "Strawberry", value: "strawberry" },
{ label: "Vanilla", value: "vanilla" },
]

export default function App() {
const {
register,
handleSubmit,
watch,
setValue,
formState: { errors },
} = useForm<{
autocomplete: NestedValue<Option[]>
select: NestedValue<number[]>
}>({
defaultValues: { autocomplete: [], select: [] },
})
const onSubmit = handleSubmit((data) => console.log(data))

useEffect(() => {
register("autocomplete", {
validate: (value) => value.length || "This is required.",
})
register("select", {
validate: (value) => value.length || "This is required.",
})
}, [register])

return (
<form onSubmit={onSubmit}>
<Autocomplete
options={options}
getOptionLabel={(option: Option) => option.label}
onChange={(e, options) => setValue("autocomplete", options)}
renderInput={(params) => (
<TextField
{...params}
error={Boolean(errors?.autocomplete)}
helperText={errors?.autocomplete?.message}
/>
)}
/>

<Select
value=""
multiple
// cast required because NestedValue<number[]> bypasses field-level type inference
onChange={(e) => setValue("select", e.target.value as number[])}
>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
</Select>

<input type="submit" />
</form>
)
}