Typescript泛型推导问题:含函数式children时Errors类型无法正确推断
解决FormHandler泛型Errors推导异常问题
问题背景
现有如下TypeScript类型定义:
import { FormikProps } from 'formik'; import React from 'react'; interface FormHandlerProps<Values, Errors> extends FormikProps<Values> { globalErrors: Errors } type FormHandlerConfig<Values, Errors> = { globalValidate?: (values: Values) => Errors children?: ((props: FormHandlerProps<Values, Errors>) => React.ReactNode) | React.ReactNode }
遇到的问题:
- 仅定义
globalValidate时,Errors泛型能正常从返回类型推导; - 同时定义
globalValidate和函数式children时,Errors会被推断为unknown; - 尤其当
globalValidate接收data参数后,其返回类型和children回调里的globalErrors类型都会变为unknown; - 只有当
children是非函数时,类型推导才正常。
解决方案
通过函数重载明确不同配置组合下的类型关联,让TypeScript优先从globalValidate的返回类型推导Errors,同时确保children的类型与之一致。调整后的组件类型定义如下:
import { FormikProps } from 'formik'; import React from 'react'; interface FormHandlerProps<Values, Errors> extends FormikProps<Values> { globalErrors: Errors; } // 重载1:带globalValidate和函数式children的场景 function FormHandler<Values, Errors>( props: Omit<FormikProps<Values>, 'children'> & { globalValidate: (values: Values) => Errors; children: (props: FormHandlerProps<Values, Errors>) => React.ReactNode; } ): React.ReactElement; // 重载2:仅带globalValidate,children为非函数的场景 function FormHandler<Values, Errors>( props: Omit<FormikProps<Values>, 'children'> & { globalValidate: (values: Values) => Errors; children?: React.ReactNode; } ): React.ReactElement; // 重载3:无globalValidate的场景 function FormHandler<Values>( props: Omit<FormikProps<Values>, 'children'> & { globalValidate?: never; children?: React.ReactNode | ((props: FormHandlerProps<Values, never>) => React.ReactNode); } ): React.ReactElement; // 组件实现签名 function FormHandler<Values, Errors>( props: Omit<FormikProps<Values>, 'children'> & { globalValidate?: (values: Values) => Errors; children?: React.ReactNode | ((props: FormHandlerProps<Values, Errors>) => React.ReactNode); } ) { // 示例实现逻辑,可根据实际需求调整 const globalErrors = props.globalValidate?.(props.values ?? props.initialValues) ?? ({} as Errors); return typeof props.children === 'function' ? props.children({ ...props, globalErrors }) : props.children ?? null; }
效果验证
调整后使用示例:
<FormHandler initialValues={initValues} onSubmit={(data) => handleSubmit(data, preferences, onSubmit)} globalValidate={(data) => { return "test"; }} validate={(data) => validate(data, preferences, initValues, onError, { isWrongModeLabel: t("preferences:text.wrong-operator"), onErrorLabel: t("preferences:confirmation.error-messages.carpool"), }) } > {({ errors, globalErrors }) => { // 此时globalErrors类型自动推导为string,符合预期 return <div>{globalErrors}</div>; }} </FormHandler>
原理说明
原类型定义中,Errors作为独立泛型参数,当globalValidate和函数式children同时存在时,TypeScript无法确定推导优先级,导致Errors被默认推断为unknown。通过重载签名,我们明确了每种配置组合的类型依赖关系,强制TypeScript优先从globalValidate的返回类型推导Errors,再将该类型传递给children回调的参数,从而解决类型推断异常问题。
内容的提问来源于stack exchange,提问作者Spartan5996
相关产品推荐
相关产品推荐

