如何为支持编辑/创建的复用表单定义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
相关产品推荐
相关产品推荐

