扩展接受道具的类型

时间:2019-09-01 20:28:59

标签: reactjs typescript

我有一个组件,其根节点可以用传递的prop的值覆盖。

interface Props {
  propA?: string
  as?: React.ElementType
}

const OverridableComponent = (props: Props) => {
  const { as: Tag = 'div', ...otherProps } = props

  return <Tag {...props} />
}

和另一个将用作as的组件。

interface SomeComponentProps {
  propB: string //<-- mandatory prop
  children?: React.ReactNode
}

const SomeComponent = (props: SomeComponentProps) => {
  return <div someProp={props.propB}>{props.children}</div>
}

所以,我想实现的结果是这样的

<OverridableComponent propA='optionalProp' as={SomeComponent} />

我希望OverridableComponent合并来自SomeComponent的道具。

<OverridableComponent propA='optionalProp' as={SomeComponent} 
propB={/*required form SomeComponentProps */'someValue />

为实现这一点,我尝试使用泛型。

type Props<T extends ElementType> = {
  propA?: string
  as?: T
} & React.ComponentProps<T>

它适用于传递as属性的情况,但如果不传递,则允许每个传递的属性,甚至from='string'都允许,因为React.ElementType的SVG属性允许它。

有什么想法可以改善这种情况吗?

1 个答案:

答案 0 :(得分:1)

您是否可以明确地将所需的React.ElementType作为Generic type arguments in JSX传递?这样,您就不必依赖编译器推断,并且可以适当地缩小类型。

组件:

const AppWithComponent = () => (
  <OverridableComponent<typeof SomeComponent>
    propA="pa"
    propB="pb"
    // className="ajsf" ; error 
    // from='string' ; error
    as={SomeComponent}
  />
);

内在元素:

const AppWithIntrinsicElementExplicit = () => (
  <OverridableComponent<"div">
    propA="pa"
    // propB="pb" ; error
    className="yehaa"
    // from='string' ; error
    as="div" // you also can drop this attribute
  />
);

Codesandbox