使用TypeScript能否基于props动态渲染带类型校验的React元素?
解决方案
你要的提取公共中央组件的实现方案完全可行,核心通过泛型联合类型+元素类型断言处理不同标签的属性类型匹配,既能完全抽离公共逻辑,又能保证TS类型校验正常,不会出现跨元素属性透传的错误。
第一步:定义泛型属性类型
import React from 'react'; import { IconDefinition } from '@fortawesome/fontawesome-svg-core'; type Variant = "primary" | "secondary" | "tertiary"; // 原有公共配置属性保留 interface CommonProps { variant?: Variant; showChevron?: boolean; icon?: IconDefinition; fullWidth?: boolean; small?: boolean; } // 支持的基础标签类型 type SupportedElement = 'a' | 'button'; // 泛型属性:根据传入的标签类型,自动匹配对应原生属性 type ComponentProps<T extends SupportedElement> = CommonProps & { element: T; // 保留你原有属性的必填约束:Link必须传href,Button必须传onClick } & (T extends 'a' ? React.AnchorHTMLAttributes<HTMLAnchorElement> & { href: string } : React.ButtonHTMLAttributes<HTMLButtonElement> & { onClick: React.MouseEventHandler<HTMLButtonElement> } );
第二步:实现公共中央组件
所有公共逻辑(样式计算、内容渲染)都在这里统一维护:
// 复用你原有逻辑即可 const initButton = (props: CommonProps) => { // 原有样式计算逻辑 return { classes: 'calculated-class-names' }; }; const Content = (props: CommonProps & { children?: React.ReactNode }) => { // 原有内容渲染逻辑:处理图标、V形标识、文字展示 return <>{props.children}</>; }; // 泛型公共组件 const Component = <T extends SupportedElement>({ element, children, className, ...rest }: ComponentProps<T>) => { const { classes } = initButton(rest); const combinedClasses = `${classes} ${className || ''}`.trim(); // 类型断言告知TS当前渲染标签和传入属性完全匹配 const Element = element as React.ElementType; return ( <Element className={combinedClasses} {...rest}> <Content {...rest}>{children}</Content> </Element> ); };
第三步:封装独立的Link和Button组件
和你期望的写法完全一致,仅做最简包装,调用时意图清晰:
// Link组件:固定渲染a标签,继承a标签所有属性约束 export const Link = (props: Omit<ComponentProps<'a'>, 'element'>) => { return <Component element="a" {...props} />; }; // Button组件:固定渲染button标签,继承button标签所有属性约束 export const Button = (props: Omit<ComponentProps<'button'>, 'element'>) => { return <Component element="button" {...props} />; };
效果说明
- 调用组件时类型校验完全正常:Link组件要求必须传入
href,Button组件要求必须传入onClick,不会出现跨标签属性误传的问题 - 后续修改公共逻辑仅需调整
Component组件,两个包装组件自动同步更新,无需重复修改代码
内容的提问来源于stack exchange,提问作者Dan Walsh
相关产品推荐
相关产品推荐

