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

如何为支持编辑/创建的复用表单定义TypeScript类型?

问题原因与解决方案

问题原因

TypeScript无法在if/else分支后正确窄化formikParams的类型,导致它被推断为两种Formik参数类型的联合类型。而useFormik钩子要求参数是单一确定的类型,无法兼容联合类型中onSubmit的函数签名差异(一个接收带id的参数,一个不带)。

具体来说,当你在两个分支分别赋值formikParams时,TypeScript会合并分支类型,形成{ initialValues: {name: string; id: string}, onSubmit: (values: ApiNameEditInput) => Promise<void>, ... } | { initialValues: {name: string}, onSubmit: (values: ApiNameCreateInput) => Promise<void>, ... }的联合类型。useFormik期望的onSubmit参数类型是单一的,联合类型下TypeScript无法确认传入的onSubmit对应哪种表单值类型,因此抛出类型不兼容错误。

解决方案

方案1:在分支内直接初始化Formik

避免使用联合类型变量,在每个分支内直接调用useFormik,让TypeScript明确当前分支的参数类型:

export const NameInputModal = (props: NameInputProps): JSX.Element => {
  const { action, isLoading, isOpen, name, onClose, onSubmit } = props;

  let formik;
  if (action === `edit`) {
    formik = useFormik({
      initialValues: {
        name,
        id: props.id,
      },
      onSubmit,
      validationSchema: toFormikValidationSchema(ApiNameEditInputSchema),
    });
  } else {
    formik = useFormik({
      initialValues: {
        name,
      },
      onSubmit,
      validationSchema: toFormikValidationSchema(ApiNameCreateInputSchema),
    });
  }

  // 模态框后续代码...
};

方案2:拆分组件分离逻辑

将创建和编辑逻辑拆分为独立组件,符合单一职责原则,类型推导更清晰:

// 创建操作组件
const CreateNameModal = ({ isLoading, isOpen, name, onClose, onSubmit }: Omit<NameInputProps, 'action' | 'id'>) => {
  const formik = useFormik({
    initialValues: { name },
    onSubmit,
    validationSchema: toFormikValidationSchema(ApiNameCreateInputSchema),
  });
  // 模态框UI代码
};

// 编辑操作组件
const EditNameModal = ({ id, isLoading, isOpen, name, onClose, onSubmit }: Omit<NameInputProps, 'action'>) => {
  const formik = useFormik({
    initialValues: { name, id },
    onSubmit,
    validationSchema: toFormikValidationSchema(ApiNameEditInputSchema),
  });
  // 模态框UI代码
};

// 入口组件
export const NameInputModal = (props: NameInputProps): JSX.Element => {
  if (props.action === 'edit') {
    return <EditNameModal {...props} />;
  }
  return <CreateNameModal {...props} />;
};

方案3:使用类型断言明确参数类型

如果需要保留单组件结构,可在调用useFormik时通过类型断言明确当前分支的参数类型:

export const NameInputModal = (props: NameInputProps): JSX.Element => {
  const { action, isLoading, isOpen, name, onClose, onSubmit } = props;

  let formikParams;
  if (action === `edit`) {
    formikParams = {
      initialValues: { name, id: props.id },
      onSubmit,
      validationSchema: toFormikValidationSchema(ApiNameEditInputSchema),
    };
  } else {
    formikParams = {
      initialValues: { name },
      onSubmit,
      validationSchema: toFormikValidationSchema(ApiNameCreateInputSchema),
    };
  }

  const formik = useFormik(
    action === 'edit' 
      ? (formikParams as Parameters<typeof useFormik>[0] & { initialValues: ApiNameEditInput })
      : (formikParams as Parameters<typeof useFormik>[0] & { initialValues: ApiNameCreateInput })
  );

  // 模态框后续代码...
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 22:57:48