对所有 HTML 元素使用类型定义
为了允许将所有 HTML 元素用作您的标签,您可以利用命名空间中IntrinsicElements
定义的接口的键JSX
。IntrinsicElements
似乎包含 HTML 元素标签到它们各自属性的映射(包括元素特定的属性)。要使用这些键,我们可以执行以下操作:
interface Props {
tag?: keyof JSX.IntrinsicElements
}
如果我想允许使用 React 组件作为标签怎么办?
React 定义了两个接口:ComponentClass
和FunctionComponent
. React 还定义了这两个接口的联合,允许您指定任何 React 组件:ComponentType
. 我们可以创建这个和我们最后一个定义的联合来允许组件和 HTML 标签。
import { ComponentType } from 'react';
interface Props {
tag?: ComponentType | keyof JSX.IntrinsicElements;
}
好吧,现在我有了一个标签,那么 HTML 属性呢?
如果您想允许所有其他 HTML 属性被允许,您可以扩展React.HTMLAttributes<Element>
以获取所有共享的 HTML 属性(没有特定于元素的属性),或者您可以引入一个泛型并利用JSX.IntrinsicElements
.
第二个选项更复杂,并带有一些注意事项。您必须使用type
而不是interface
扩展/交叉您Props
和JSX.IntrinsicElements
. 您还需要在您的函数上使用泛型,以便您可以将它们传递给您的Props
类型,这意味着您不能再使用,React.FunctionComponent<Props>
因为这发生在访问任何泛型之前。这意味着您需要添加children
到您的Props
定义中。
我认为用这个例子更好地解释了很多词:
// Define our Props type to allow the specifying of a Tag for HTML attributes
// Also define children as React does with React.ReactNode
type Props<Tag extends keyof JSX.IntrinsicElements> = {
tag?: ComponentType | keyof JSX.IntrinsicElements;
children?: ReactNode;
} & JSX.IntrinsicElements[Tag];
// Define our generic (Tag) again here and give it our default value
// Don't forget to specify the type Props<Tag> at the end of your function's arguments
// Then we can spread all props to the tag/Wrapper
function MyComponent<Tag extends keyof JSX.IntrinsicElements = 'div'>({ tag: Wrapper = 'div', ...props }: Props<Tag>) {
return <Wrapper {...props} />;
}
// Example usage, noValidate is typed as
// (JSX attribute) React.FormHTMLAttributes<HTMLFormElement>.noValidate?: boolean | undefined
<MyComponent<'form'> tag="form" noValidate>
{/* My Form Stuff */}
</MyComponent>;
// You don't need to specify 'div' since it is the default
<MyComponent id="page">
<p>Just a paragraph inside of a regular div</p>
</MyComponent>;