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

自定义Chakra-UI组件样式重载函数引发TS-2590联合类型错误

解决Chakra UI工厂函数的TS-2590联合类型过复杂错误

我实现了一个类似@chakra-ui/react中chakra()的工厂函数,能重载目标组件的任意属性,功能正常,但触发了TS-2590错误,提示“Expression produces a union type that is too complex to represent”。

原问题代码

import { As, chakra, ChakraComponent, Stack } from '@chakra-ui/react';
import { ComponentProps, ForwardedRef, forwardRef, ForwardRefExoticComponent, PropsWithoutRef } from 'react';

function makeChakra<
  T extends As,
  P = {},
  FP = PropsWithoutRef<ComponentProps<T> & P> ,
  FPR = ForwardRefExoticComponent<FP & ComponentProps<ChakraComponent<T>>>
>(
  comp: T,
  label?: string
): (
  props?: Partial<ComponentProps<ChakraComponent<T>>>
) => FPR {
  const Component = chakra(comp, {
    label,
  });

  return (props = {}) =>
    // eslint-disable-next-line react/display-name
    (forwardRef<ChakraComponent<T>, ComponentProps<T>>(
      (
        innerProps: ComponentProps<T>,
        ref: ForwardedRef<ChakraComponent<T>>
      ) => {
        return <Component ref={ref} {...props} {...innerProps} />;
      }
    ) as FPR);
}

// 触发TS-2590错误的位置
export const CustomStack = makeChakra(
    Stack,
    "CustomStack"
)({
    pos: "relative",
    spacing: 24,
    m: 20,
    placeItems: "stretch",
    placeContent: "stretch",
    lineHeight: "shorter",
});

错误原因

  1. 泛型层级嵌套过深:ComponentProps<ChakraComponent<T>>这类嵌套类型推导会让TypeScript无法处理过于复杂的联合类型组合
  2. 手动定义的FPR泛型过度合并了多个复杂类型,导致类型推导爆炸,超出TypeScript的处理上限

解决方案:简化泛型与类型推导

通过拆分泛型层级、复用Chakra内置类型、减少不必要的类型嵌套,来降低类型推导的复杂度:

优化后的代码

import { As, chakra, ChakraComponent, Stack, ChakraProps } from '@chakra-ui/react';
import { ComponentProps, ForwardedRef, forwardRef, ForwardRefExoticComponent, PropsWithoutRef } from 'react';

// 拆分泛型,将默认属性的泛型延迟到返回函数中,减少单次推导压力
function makeChakra<T extends As>(
  comp: T,
  label?: string
) {
  const BaseComponent = chakra(comp, { label });

  return <P extends Record<string, any> = {}>(
    defaultProps?: Partial<ChakraProps<T>>
  ): ForwardRefExoticComponent<PropsWithoutRef<ComponentProps<T> & P> & { ref?: ForwardedRef<ChakraComponent<T>> }> => {
    // eslint-disable-next-line react/display-name
    return forwardRef((innerProps: ComponentProps<T> & P, ref: ForwardedRef<ChakraComponent<T>>) => {
      return <BaseComponent ref={ref} {...defaultProps} {...innerProps} />;
    }) as any; // 临时用any简化断言,追求严谨可进一步用Extract/Pick缩小类型范围
  };
}

// 使用示例,无TS报错
export const CustomStack = makeChakra(Stack, "CustomStack")({
  pos: "relative",
  spacing: 24,
  m: 20,
  placeItems: "stretch",
  placeContent: "stretch",
  lineHeight: "shorter",
});

更简洁的实现(复用Chakra内置类型工具)

如果不需要过度自定义类型,可以直接复用Chakra的CreateStyledComponent类型,进一步简化代码:

import { As, chakra, Stack, ChakraProps, CreateStyledComponent } from '@chakra-ui/react';

function makeChakra<T extends As>(comp: T, label?: string) {
  return <P = {}>(defaultProps?: Partial<ChakraProps<T>>): CreateStyledComponent<T, P> => {
    const Component = chakra(comp, { label });
    return (props: any) => <Component {...defaultProps} {...props} /> as any;
  };
}

export const CustomStack = makeChakra(Stack, "CustomStack")({
  pos: "relative",
  spacing: 24,
  m: 20,
  placeItems: "stretch",
  placeContent: "stretch",
  lineHeight: "shorter",
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 03:45:47