为何我的React+TS组件复杂泛型类型允许任意属性,削弱类型安全性?
解决React组件Props类型过于宽松的问题
问题根源
你当前的类型定义使用了交叉联合类型(CustomComponentPropsBase & (A | B)),这种结构会让TypeScript在推断剩余props的类型时出现宽化,导致无法有效约束非法属性的访问。
修复后的类型定义
import { ElementType, ComponentPropsWithoutRef, ReactElement, JSX } from 'react'; // 先明确Size类型(根据业务场景调整) type Size = 'small' | 'default' | 'large'; // 改用区分联合类型,让TypeScript能精准推断每个分支的类型 type CustomComponentProps<Component extends ElementType = "div"> = // 分支1:不使用asChild,禁止传入children ({ size?: Size; asChild?: false; children?: never; } & ComponentPropsWithoutRef<Component>) // 分支2:使用asChild,必须传入单个ReactElement子元素 | ({ size?: Size; asChild: true; children: ReactElement; } & ComponentPropsWithoutRef<Component>); export default function CustomComponent<Component extends ElementType = "div">( props: CustomComponentProps<Component> ): JSX.Element { if (props.asChild) { const { size = "default", children, ...restProps } = props; // 此处restProps严格对应Component的Props,非法属性会触发类型错误 return React.cloneElement(children, { ...restProps, size }); } else { const { size = "default", ...restProps } = props; // 同样,restProps类型严格约束,确保类型安全 const ComponentToRender = Component as ElementType; return <ComponentToRender {...restProps} size={size} />; } }
关键优化点
- 改用区分联合类型:以
asChild作为区分符,让TypeScript能清晰识别每个分支的属性约束,避免类型宽化。 - 分支内精准解构:在
if (props.asChild)的类型守卫分支里,剩余restProps会被自动推断为对应Component的ComponentPropsWithoutRef类型,非法属性访问会立刻触发类型错误。 - 移除冗余交叉:直接将每个分支与
ComponentPropsWithoutRef<Component>交叉,替代原来的嵌套交叉联合结构,提升类型推断的准确性。
验证效果
现在当你尝试访问restProps.whatever这类不存在的属性时,TypeScript会直接抛出类型错误,完全符合你对类型安全的要求,同时保留了组件的灵活性:
- 支持切换不同HTML元素或自定义组件
size属性正常生效asChild为false时无法传入children,为true时必须传入单个ReactElement- 完整继承底层组件的所有Props
内容的提问来源于stack exchange,提问作者Amanda Ferrari
相关产品推荐
相关产品推荐

