React-TypeScript中自动推导forwardRef元素类型的实现方案
自动推导forwardRef中透传元素的类型
可以实现自动推导被透传HTML元素的类型,核心思路是利用TypeScript的条件类型结合React内置的类型工具,提取元素对应的Ref类型,以下是具体实现方案:
1. 核心工具类型定义
借助React内置的类型体系,我们可以封装出通用的类型提取工具:
// 提取指定HTML元素/组件对应的Ref类型 type ExtractElementRef<T extends keyof React.JSX.IntrinsicElements | React.ComponentType<any>> = T extends React.ComponentType<any> ? React.ElementRef<T> : React.JSX.IntrinsicElements[T]['ref'] extends React.Ref<infer R> ? R : never; // 提取指定元素包含ref的完整Props类型 type ExtractElementProps<T extends keyof React.JSX.IntrinsicElements | React.ComponentType<any>> = React.ComponentPropsWithRef<T>;
2. 通用组件生成方案
如果需要批量封装组件,可以基于上述工具类型写一个通用生成函数,自动推导Ref和Props:
import { forwardRef, ReactNode } from "react"; function createStyledElement<T extends keyof React.JSX.IntrinsicElements>( tagName: T, baseClassName: string ) { type ComponentProps = Omit<ExtractElementProps<T>, 'className'> & { children?: ReactNode; }; type ComponentRef = ExtractElementRef<T>; return forwardRef<ComponentRef, ComponentProps>((props, ref) => { const { children, ...rest } = props; return React.createElement(tagName, { ref, className: baseClassName, ...rest }, children); }); } // 使用示例:自动推导FancyButton的Ref为HTMLButtonElement export const FancyButton = createStyledElement('button', 'MyClassName'); // 同理可快速生成其他组件 export const FancyInput = createStyledElement('input', 'InputClassName');
3. 单个组件的简化写法
如果只需要封装单个组件,也可以直接在组件内自动推导类型,无需额外工具函数:
import { forwardRef, ReactNode, ComponentPropsWithRef, ElementRef } from "react"; // 提取button的Props并排除className(自定义样式) type ButtonProps = Omit<ComponentPropsWithRef<'button'>, 'className'> & { children?: ReactNode; }; // 自动推导Ref类型为HTMLButtonElement type ButtonRef = ElementRef<'button'>; export const FancyButton = forwardRef<ButtonRef, ButtonProps>((props, ref) => ( <button ref={ref} className="MyClassName" {...props}> {props.children} </button> ));
原理说明
React.JSX.IntrinsicElements包含所有HTML元素的属性定义,通过keyof可获取'button'、'input'等元素标识React.ElementRef<T>直接提取元素对应的Ref类型,比如ElementRef<'button'>等价于HTMLButtonElementReact.ComponentPropsWithRef<T>提取元素包含ref的完整Props类型,方便通过Omit剔除需要自定义的属性
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

