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

TypeScript接口中如何定义React组件的函数类型props

React + TypeScript 回调Prop类型定义与react-hook-form重置实现

你之前定义onSaveExpenseData:()=>void不生效的核心原因是:这个类型声明代表该函数不需要接收任何参数,但实际业务中你需要在ExpenseForm提交表单时,把收集到的表单数据作为参数传入这个回调,参数类型不匹配就会导致TS报错。

以下是完整的实现方案:

1. 抽离公共类型

把多个组件复用的数据类型统一抽离,避免重复定义:

// 可以单独存为types.ts,三个组件共同引入
export interface ExpenseFormInput {
  enteredTitle: string;
  enteredAmount: number;
  enteredDate: string;
}

// 最终生成的费用项,在表单数据基础上增加id字段
export interface ExpenseItem extends ExpenseFormInput {
  id: string;
}

2. 修复ExpenseForm组件类型与表单重置逻辑

// ExpenseForm.tsx
import { useState } from 'react';
import { useForm } from 'react-hook-form';
import type { ExpenseFormInput } from './types';

// 定义组件接收的Props类型
interface ExpenseFormProps {
  // 明确回调函数需要接收ExpenseFormInput类型的参数,无返回值
  onSaveExpenseData: (formData: ExpenseFormInput) => void;
}

const ExpenseForm = (props: ExpenseFormProps) => {
  const [userInput, setUserInput] = useState<ExpenseFormInput>({
    enteredTitle: "",
    enteredAmount: 10,
    enteredDate: "",
  });

  const { register, handleSubmit, reset } = useForm<ExpenseFormInput>({
    // 配置表单默认值,重置时会回到该状态
    defaultValues: {
      enteredTitle: "",
      enteredAmount: 10,
      enteredDate: ""
    }
  });

  const onValid = (data: ExpenseFormInput) => {
    setUserInput(data);
    // 调用父组件传入的回调,传递表单数据
    props.onSaveExpenseData(data);
    // 无需逐个调用resetField,直接调用reset一次性重置所有字段
    reset();
  };

  return (
    <form onSubmit={handleSubmit(onValid)}>
      <div>
        <label>费用名称</label>
        <input {...register("enteredTitle", { required: true })} />
      </div>
      <div>
        <label>金额</label>
        <input type="number" {...register("enteredAmount", { required: true, min: 0.01 })} />
      </div>
      <div>
        <label>日期</label>
        <input type="date" {...register("enteredDate", { required: true })} />
      </div>
      <button type="submit">添加费用</button>
    </form>
  );
};

export default ExpenseForm;

3. 修复NewExpense组件类型

// NewExpense.tsx
import ExpenseForm from './ExpenseForm';
import type { ExpenseFormInput, ExpenseItem } from './types';

interface NewExpenseProps {
  onAddExpense: (expense: ExpenseItem) => void;
}

const NewExpense = (props: NewExpenseProps) => {
  const saveExpenseDataHandler = (enteredExpenseData: ExpenseFormInput) => {
    const expenseData: ExpenseItem = {
      ...enteredExpenseData,
      id: Math.random().toString(),
    };
    console.log(expenseData);
    props.onAddExpense(expenseData);
  };
  return (
    <div className="new-expense">
      <ExpenseForm onSaveExpenseData={saveExpenseDataHandler} />
    </div>
  );
};

export default NewExpense;

4. 修复App组件类型

//App.tsx
import NewExpense from './NewExpense';
import type { ExpenseItem } from './types';

const App = () => {
  const expenses: ExpenseItem[] = [
    {
      id: "xxxx",
      enteredTitle: "xxx",
      enteredAmount: 100,
      enteredDate: "2022-06-16",
    },
  ];

  const addExpenseHandler = (expense: ExpenseItem) => {
    console.log(expense);
    // 此处可更新expenses数组渲染列表
  };

  return (
    <div>
      <NewExpense onAddExpense={addExpenseHandler} />
    </div>
  );
};

export default App;

关键说明

  • 函数类型定义必须和实际调用时的参数、返回值完全匹配,给回调传参就必须在类型里声明对应的参数类型。
  • react-hook-form的reset()方法比逐个调用resetField更适合全表单重置场景,配合defaultValues配置可以精准控制重置后的初始状态。
  • 跨组件复用的类型建议抽离到独立文件维护,避免重复定义产生的类型冲突。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.01 23:21:43