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

自定义Form Hook的TypeScript类型标注问题

自定义Form Hook的TypeScript类型标注问题(useReducer相关)

问题背景

我已掌握TypeScript基础,但在给useReducer这类高级概念加类型标注时卡壳了。下面是我写的自定义Form Hook代码,应该存在类型标注错误:

import { useCallback, useReducer } from 'react';

interface Inputs {
  id: string;
  value: string;
  inputIsValid: boolean;
}

interface FormState {
  inputs: Inputs[];
  isValid: boolean;
}

interface FormAction {
  inputs: Inputs;
  type: string;
  value: string;
  inputId: string;
  isValid: boolean;
  formIsValid: boolean;
}

const formReducer = (state: FormState, action: FormAction): FormState => {
  switch (action.type) {
    case 'INPUT_CHANGE':
      let formIsValid = true;
      for (const inputId in state.inputs) {
        if (!state.inputs[inputId]) continue;
        if (inputId === action.inputId)
          formIsValid = formIsValid && action.isValid;
        else formIsValid = formIsValid && state.inputs[inputId].inputIsValid;
      }
      return {
        ...state,
        inputs: {
          ...state.inputs,
          [action.inputId]: { value: action.value, isValid: action.isValid },
        },
        isValid: formIsValid,
      };
    case 'SET_DATA':
      return {
        inputs: action.inputs,
        isValid: action.formIsValid,
      };
    default:
      return state;
  }
};

export const useForm = (
  initialInputs: Inputs,
  initialFormValidity: boolean
) => {
  const [formState, dispatch] = useReducer(formReducer, {
    inputs: initialInputs,
    isValid: initialFormValidity,
  });
  const inputHandler = useCallback(
    (id: string, value: string, isValid: boolean) => {
      dispatch({
        type: 'INPUT_CHANGE',
        value: value,
        isValid: isValid,
        inputId: id,
      });
    },
    []
  );
  const setFormData = useCallback(
    (inputData, formValidity: boolean) => {
      dispatch({
        type: 'SET_DATA',
        inputs: inputData,
        formIsValid: formValidity,
      });
    },
    []
  );
  return [formState, inputHandler, setFormData];
};

我还有一个用这个Hook的自定义Input组件,代码来自Max Schwarzmueller的MERN栈课程(课程没给类型标注)。我学过他的TypeScript课但还没看完,核心问题出在useReducer上,报错信息如下:

报错信息

No overload matches this call.
  Overload 1 of 5, '(reducer: ReducerWithoutAction<any>, initializerArg: any, initializer?: undefined): [any, DispatchWithoutAction]', gave the following error.
    Argument of type '(state: FormState, action: FormAction) => FormState' is not assignable to parameter of type 'ReducerWithoutAction<any>'.
  Overload 2 of 5, '(reducer: (state: FormState, action: FormAction) => FormState, initialState: FormState, initializer?: undefined): [FormState, Dispatch<...>]', gave the following error.
    Type 'Inputs' is not assignable to type 'Inputs[]'.ts(2769)
form-hook.tsx(10, 3): The expected type comes from property 'inputs' which is declared here on type 'FormState'

解决方法

我调整了两处关键类型定义,解决了问题:

  • 把useForm函数的initialInputs参数类型从Inputs改成Inputs[],和FormState里inputs的数组类型匹配
  • 将FormAction从单一接口改成联合类型,明确每个action的专属结构,去掉冗余属性:
type FormAction =
  | { type: 'INPUT_CHANGE'; value: string; inputId: string; isValid: boolean }
  | { type: 'SET_DATA'; formIsValid: boolean; inputs: Inputs[] };

这个方案能很好地解决useReducer的类型不匹配问题,推荐给所有想搞懂TypeScript中useReducer用法的开发者。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 15:35:30