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

TypeScript自定义Hook参数类型关联问题求助

解决方案:让useForm的watch类型随Hook参数动态匹配

问题根源分析

  1. 泛型类型收窄失效:在if (product === 'c')分支中,TypeScript无法自动将qp的类型收窄为cq,因为泛型T的类型没有和分支条件关联,导致qp仍被视为kq | cq,访问c或t属性时触发类型错误。
  2. useForm类型推断错误:未明确指定useForm的泛型参数,导致其根据generatedQP的联合类型错误推断最终表单类型,消费端watch的类型无法正确关联传入的product参数。
  3. 消费端类型未具体化:将p定义为p类型('k' | 'c'),导致泛型T被推断为联合类型,无法得到具体的表单类型。

修改后的完整代码

自定义Hook代码

import { useMemo } from 'react';
import { useForm, UseFormReturn } from 'react-hook-form'

type ProductType = 'k' | 'c';

interface KFormValues {
  s: string;
  t: string;
  ce: string;
}

interface CFormValues {
  s: string;
  c: string;
  ce: string;
}

// 用映射类型替代条件类型,更直观关联产品类型和表单值类型
type FormValuesMap = {
  k: KFormValues;
  c: CFormValues;
};

// 明确Hook返回类型为对应产品的useForm返回值
function useMyHook<T extends ProductType>(product: T, qp?: FormValuesMap[T]): UseFormReturn<FormValuesMap[T]> {
  const generatedQP = useMemo(() => {
    if (product === 'c') {
      // 断言qp为CFormValues类型,解决分支内属性访问错误
      const cQp = qp as CFormValues | undefined;
      return {
        s: cQp?.s || '',
        ce: cQp?.ce || 'default',
        c: cQp?.c || ''
      } as const;
    } else {
      // 断言qp为KFormValues类型
      const kQp = qp as KFormValues | undefined;
      return {
        s: kQp?.s || '',
        ce: kQp?.ce || 'default',
        t: kQp?.t || ''
      } as const;
    }
  }, [product, qp]);

  // 明确指定useForm的泛型参数为对应产品的表单值类型
  const methods = useForm<FormValuesMap[T]>({
    criteriaMode: 'all', // 修复原代码拼写错误:criterialMode → criteriaMode
    defaultValues: generatedQP
  });

  return methods;
}

消费端代码

function Component() {
  // 方式1:直接传入字面量,自动推断类型
  const { watch } = useMyHook('c');
  const formValues = watch();
  // 此时formValues类型为CFormValues,可安全访问c属性
  console.log(formValues.c);

  // 方式2:使用const断言固定变量类型(需用变量传递时)
  const p = 'k' as const;
  const { watch: kWatch } = useMyHook(p);
  const kFormValues = kWatch();
  // 此时kFormValues类型为KFormValues,可安全访问t属性
  console.log(kFormValues.t);

  return null;
}

关键修改点说明

  1. 映射类型替代条件类型:用FormValuesMap将产品类型和表单值类型做直接映射,比条件类型更清晰,TypeScript更容易进行类型关联。
  2. 分支内类型断言:在if/else分支中,通过as断言将qp收窄为对应产品的表单值类型,解决属性访问的类型错误。
  3. 明确useForm泛型参数:手动指定useForm<FormValuesMap[T]>,确保表单类型与传入的product参数严格绑定,让watch返回正确的类型。
  4. 消费端类型具体化:避免将参数定义为联合类型ProductType,而是直接传入字面量或用as const固定变量类型,确保泛型T被推断为具体的产品类型。
  5. 修复拼写错误:原代码中criterialMode是拼写错误,正确应为criteriaMode,否则react-hook-form的配置不会生效。

内容的提问来源于stack exchange,提问作者CCCXXX

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 10:03:30