如何键入一个接受另一个组件作为道具的 React 组件,以及该组件的所有道具?

Vin*_*ent 3 emotion typescript reactjs

我有一个<Wrapper>可以按如下方式使用的 React 组件:

// Assuming Link is a component that takes a `to` prop: <Link to="/somewhere">label</Link>
<Wrapped as={Link} to="/somewhere">label</Wrapped>
Run Code Online (Sandbox Code Playgroud)

如果没有as传递 prop,它将假定一个<a>. 但是如果一个组件被传递给as,那么对该组件有效的所有道具现在也应该是 的有效道具Wrapped

有没有办法在 TypeScript 中输入这个?我目前正在考虑以下方面:

type Props<El extends JSX.Element = React.ReactHTMLElement<HTMLAnchorElement>> = { as: El } & React.ComponentProps<El>;
const Wrapped: React.FC<Props> = (props) => /* ... */;
Run Code Online (Sandbox Code Playgroud)

但是,我不确定这里是否JSX.Element和/或是React.ComponentProps相关类型,并且无法编译,因为El无法传递给ComponentProps. 正确的类型是什么,这样的事情甚至可能吗?

bsa*_*aka 5

您需要的关系是ComponentTypeElementType

import React, { ComponentType, ElementType, ReactNode } from 'react';

type WrappedProps <P = {}> = { 
  as?: ComponentType<P> | ElementType
} & P

function Wrapped<P = {}>({ as: Component = 'a', ...props }: WrappedProps<P>) {
  return (
    <Component {...props} />
  );
}
Run Code Online (Sandbox Code Playgroud)

有了它,你就可以做到:

interface LinkProps {
  to: string,
  children: ReactNode,
}
function Link({ to, children }: LinkProps) {
  return (
    <a href={to}>{children}</a>
  );
}

function App() {
  return (
    <div>
      <Wrapped<LinkProps> as={Link} to="/foo">Something</Wrapped>
      <Wrapped as="div" style={{ margin: 10 }} />
      <Wrapped />
    </div>
  );
}
Run Code Online (Sandbox Code Playgroud)

  • 极好的!这正是我一直在寻找的。即使相对简单,也很棒。 (2认同)