自定义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", });
错误原因
- 泛型层级嵌套过深:
ComponentProps<ChakraComponent<T>>这类嵌套类型推导会让TypeScript无法处理过于复杂的联合类型组合 - 手动定义的
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
相关产品推荐
相关产品推荐

