如何键入一个接受另一个组件作为道具的 React 组件,以及该组件的所有道具?
How to type a React component that accepts another component as a prop, and all of that component's props?
我有一个 React 组件 <Wrapper>
,我可以按如下方式使用它:
// Assuming Link is a component that takes a `to` prop: <Link to="/somewhere">label</Link>
<Wrapped as={Link} to="/somewhere">label</Wrapped>
如果没有传递 as
属性,它将假设一个 <a>
。但是如果一个组件被传递给 as
,所有对该组件有效的 props 现在也应该是 Wrapped
.
的有效 props
有没有办法在 TypeScript 中输入这个?我目前正在沿着这些思路思考:
type Props<El extends JSX.Element = React.ReactHTMLElement<HTMLAnchorElement>> = { as: El } & React.ComponentProps<El>;
const Wrapped: React.FC<Props> = (props) => /* ... */;
但是,我不确定JSX.Element
and/or React.ComponentProps
是否是这里的相关类型,并且这不会编译,因为 El
无法传递给ComponentProps
。正确的类型是什么,这样的事情是否可能?
我认为这可能是这样的:
import React from 'React';
type WrappedProps = {
as: React.ReactNode,
to: string;
}
参考:https://github.com/typescript-cheatsheets/react-typescript-cheatsheet
这可能适合你:
type Props = {
as?: React.FC<{ to: string, children: any }>,
to: string,
children: any
}
const Wrapped: React.FC<Props> = ({ as: El, to, children }) => (
El
? <El to={to}>{children}</El>
: <a href={to}>{children}</a>
)
然后你可以使用 as
prop:
<Wrapped as={Link} to="/somewhere">label</Wrapped>
...或不默认为 <a>
元素:
<Wrapped to="/somewhere">label</Wrapped>
根据您在下面的评论,如果您希望 Wrapped
接受传递给 as
道具的组件的道具,那么我不确定如何执行此操作或是否可行。
不过,您可以尝试这样的操作:
<Wrapped as={<Link to="somewhere">{label}</Link>} />
...所以以这种方式将 Link
传递给 as
道具意味着您不需要将道具传递给 Wrapped
.
type Props = {
as?: React.ReactNode
children: any
}
const Wrapped: React.FC<Props> = ({ as, children }) => (
<>{ as ? as : <a href="/somewhere">{children}</a>}</>
)
您需要的类型是 ComponentType
和 ElementType
。
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} />
);
}
有了它,您可以做到:
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>
);
}
我有一个 React 组件 <Wrapper>
,我可以按如下方式使用它:
// Assuming Link is a component that takes a `to` prop: <Link to="/somewhere">label</Link>
<Wrapped as={Link} to="/somewhere">label</Wrapped>
如果没有传递 as
属性,它将假设一个 <a>
。但是如果一个组件被传递给 as
,所有对该组件有效的 props 现在也应该是 Wrapped
.
有没有办法在 TypeScript 中输入这个?我目前正在沿着这些思路思考:
type Props<El extends JSX.Element = React.ReactHTMLElement<HTMLAnchorElement>> = { as: El } & React.ComponentProps<El>;
const Wrapped: React.FC<Props> = (props) => /* ... */;
但是,我不确定JSX.Element
and/or React.ComponentProps
是否是这里的相关类型,并且这不会编译,因为 El
无法传递给ComponentProps
。正确的类型是什么,这样的事情是否可能?
我认为这可能是这样的:
import React from 'React';
type WrappedProps = {
as: React.ReactNode,
to: string;
}
参考:https://github.com/typescript-cheatsheets/react-typescript-cheatsheet
这可能适合你:
type Props = {
as?: React.FC<{ to: string, children: any }>,
to: string,
children: any
}
const Wrapped: React.FC<Props> = ({ as: El, to, children }) => (
El
? <El to={to}>{children}</El>
: <a href={to}>{children}</a>
)
然后你可以使用 as
prop:
<Wrapped as={Link} to="/somewhere">label</Wrapped>
...或不默认为 <a>
元素:
<Wrapped to="/somewhere">label</Wrapped>
根据您在下面的评论,如果您希望 Wrapped
接受传递给 as
道具的组件的道具,那么我不确定如何执行此操作或是否可行。
不过,您可以尝试这样的操作:
<Wrapped as={<Link to="somewhere">{label}</Link>} />
...所以以这种方式将 Link
传递给 as
道具意味着您不需要将道具传递给 Wrapped
.
type Props = {
as?: React.ReactNode
children: any
}
const Wrapped: React.FC<Props> = ({ as, children }) => (
<>{ as ? as : <a href="/somewhere">{children}</a>}</>
)
您需要的类型是 ComponentType
和 ElementType
。
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} />
);
}
有了它,您可以做到:
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>
);
}