You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Hook Form泛型单选按钮组件TypeScript类型报错求助

泛型单选按钮结合React Hook Form的TypeScript类型问题排查

问题背景

已实现可正常运行的泛型单选按钮组件,结合React Hook Form做表单校验时出现TypeScript类型报错,移除泛型则功能正常,需排查解决。

组件代码

import React, { ReactNode } from 'react';
import styled from 'styled-components/native';

export interface RadioButtonProp<T> {
    data: T[]
    value?: T
    render: (item: T, index?: number) => ReactNode
}

export const RadioButton = <T,>(props: RadioButtonProp<T>) => {
    const { data, render } = props
    return (
        <Container>
            {data.map((item: T, index: number) => {
                return <RowContainer key={index.toString()}>{render(item, index)}</RowContainer>
            })}
        </Container>
    )
}

const Container = styled.View`
    margin-top: 5px;
`

const RowContainer = styled.View``

React Hook Form封装代码

import { FieldValues, UseControllerProps, Controller } from "react-hook-form";
import { RadioButton, RadioButtonProp } from './RadioButton';

export type RfhBadioButtonProp<T extends FieldValues> = RadioButtonProp<T> & UseControllerProps<T>

export const RhfRadioButton = <T extends FieldValues>(props: RfhBadioButtonProp<T>) => {
    return (
        <Controller
            rules={props.rules}
            control={props.control}
            name={props.name}
            render={({ field }) => (
                <>
                    <RadioButton
                        data={props.data}
                        render={props.render}
                        value={props.value || field.value}
                    />
                </>
            )}
        />
    )
}

表单使用代码

// 假设Fruit为表单类型,useForm初始化示例:
type Fruit = {
  fruits: string;
};
const { control } = useForm<Fruit>();

<RhfRadioButton
   control={control}
   name={'fruits'}
   data={['Apple', 'Oranges', 'Banana']}
   render={renderRadioButtons}
   value={fruit}
/>

TypeScript报错信息

Type 'Control<Fruit, any>' is not assignable to type 'Control<FieldValues, any>'.
  The types of '_options.resolver' are incompatible between these types.
    Type 'Resolver<Fruit, any> | undefined' is not assignable to type 'Resolver<FieldValues, any> | undefined'.
      Type 'Resolver<Fruit, any>' is not assignable to type 'Resolver<FieldValues, any>'

问题原因

原封装代码的泛型约束错误:将单选按钮的选项类型直接约束为FieldValues(表单整体类型),导致React Hook Form的Control<表单类型>与Control<选项类型>类型不兼容,触发类型校验报错。

解决方案

拆分泛型,分别指定表单整体类型和单选选项类型,修正封装代码如下:

import { FieldValues, UseControllerProps, Controller } from "react-hook-form";
import { RadioButton, RadioButtonProp } from './RadioButton';

// 拆分泛型:T为表单FieldValues类型,V为单选选项类型
export type RhfRadioButtonProp<T extends FieldValues, V> = RadioButtonProp<V> & Omit<UseControllerProps<T>, 'render'>;

export const RhfRadioButton = <T extends FieldValues, V>(props: RhfRadioButtonProp<T, V>) => {
    return (
        <Controller
            rules={props.rules}
            control={props.control}
            name={props.name}
            render={({ field }) => (
                <RadioButton
                    data={props.data}
                    render={props.render}
                    value={props.value ?? field.value}
                />
            )}
        />
    )
}

关键修改说明

  1. 泛型拆分:新增泛型V用于定义单选选项的类型,T保留为表单整体的FieldValues类型,解决类型不匹配问题。
  2. Omit冗余属性:通过Omit<UseControllerProps<T>, 'render'>移除UseControllerProps中的render属性,避免与组件自身的render属性冲突。
  3. 空值处理优化:用??替代||,更严谨处理0、''等 falsy 值场景。

修改后,表单中使用组件时无需手动指定泛型,TypeScript会自动推断T(表单类型)和V(选项类型),类型校验正常通过。

内容的提问来源于stack exchange,提问作者EI-01

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 21:30:52