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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 06:33:27