如何为带as属性的React组件实现TypeScript类型继承与提示
问题描述
想要创建带有as属性的MyComponent,使其继承as指定组件/元素的属性,并获得TypeScript类型支持:
使用示例
HTML元素场景:
<MyComponent as="label" htmlFor="caption" /* 应从label自动补全 */ asdf="" /* 应提示错误 */ className="test" > Test </MyComponent>
React组件场景:
<MyComponent as={MyGrid} rows={4} /* 应从MyGrid自动补全 */ asdf="" /* 应提示错误 */ > Test </MyComponent>
目前已实现组件功能,但无法继承类型,现有代码如下:
import { useRef, forwardRef } from "react"; import type { PropsWithChildren, ElementType, ComponentProps, ForwardedRef, ReactElement, } from "react"; type MyProps<T extends ElementType> = ComponentProps<T> & PropsWithChildren & { as?: T }; function _MyComponent<T extends ElementType = "div">( { as, children, ...props }: MyProps<T>, ref: ForwardedRef<HTMLElement>, ): ReactElement<T> { const Comp = as ?? "div"; return ( <Comp {...props} ref={ref}> {children} </Comp> ); } const MyComponent = forwardRef(_MyComponent); export default MyComponent;
请问需要修改哪些TypeScript代码以实现类型提示?
解决方案
现有代码的核心问题在于:
MyProps的类型合并逻辑未建立as属性与目标组件/元素属性的关联,导致TypeScript无法动态推断属性集合ref类型固定为HTMLElement,未跟随as指定的元素/组件动态变化
需要做以下3处修改:
1. 调整Props类型定义
将MyProps改为条件组合类型,排除目标组件可能存在的as属性冲突,同时明确as与属性集合的关联:
type MyProps<T extends ElementType> = PropsWithChildren< Omit<ComponentProps<T>, "as"> & { as?: T } >;
2. 动态推导Ref类型
使用ComponentPropsWithRef获取目标组件/元素对应的Ref类型,替换固定的HTMLElement:
// 组件参数中修改ref类型 ref: ForwardedRef<ComponentPropsWithRef<T>["ref"]>
3. 修正返回值类型
原ReactElement<T>类型不准确,直接返回ReactElement即可,TypeScript会自动推导正确类型。
完整修正代码
import { forwardRef } from "react"; import type { PropsWithChildren, ElementType, ComponentProps, ForwardedRef, ReactElement, ComponentPropsWithRef, } from "react"; type MyProps<T extends ElementType> = PropsWithChildren< Omit<ComponentProps<T>, "as"> & { as?: T } >; function _MyComponent<T extends ElementType = "div">( { as, children, ...props }: MyProps<T>, ref: ForwardedRef<ComponentPropsWithRef<T>["ref"]> ): ReactElement { const Comp = as ?? "div"; return ( <Comp {...props} ref={ref}> {children} </Comp> ); } const MyComponent = forwardRef(_MyComponent); export default MyComponent;
验证效果
- 指定
as="label"时,会自动提示htmlFor等label元素专属属性,输入非法属性(如asdf)会触发TypeScript错误 - 指定
as={MyGrid}时,会自动补全MyGrid组件的所有属性,非法属性同样会报错
内容的提问来源于stack exchange,提问作者Kernel James
相关产品推荐
相关产品推荐

