如何在带有TypeScript的React中键入动态标签?给出以下代码:
interface CompProps {
tag: string;
}
const MyComponent: React.FunctionComponent<CompProps> = ({
tag = "div",
children
}) => {
const Wrapper = tag;
return <Wrapper>{children}</Wrapper>;
};
我收到此错误:
类型'{子代:ReactNode; }”与类型“ IntrinsicAttributes”没有共同的属性。 ts(2559)
在我看来,我必须添加适当的类型,但是我无法弄清楚哪种类型。
答案 0 :(得分:5)
为了允许将所有 HTML 元素用作您的标签,您可以利用 IntrinsicElements
命名空间中定义的 JSX
接口的键。 IntrinsicElements
似乎包含 HTML 元素标签到它们各自属性的映射(包括元素特定的属性)。要使用这些键,我们可以执行以下操作:
interface Props {
tag?: keyof JSX.IntrinsicElements
}
React 定义了两个接口:ComponentClass
和 FunctionComponent
。 React 还定义了这两个接口的联合,允许您指定任何 React 组件:ComponentType
。我们可以创建这个和我们最后一个定义的联合来允许组件和 HTML 标签。
import { ComponentType } from 'react';
interface Props {
tag?: ComponentType | keyof JSX.IntrinsicElements;
}
如果您想允许所有其他 HTML 属性被允许,您可以扩展 React.HTMLAttributes<Element>
以获取所有共享的 HTML 属性(没有特定于元素的属性),或者您可以引入一个泛型并利用 {{1} }}。
第二个选项更复杂,并带有一些注意事项。 您必须使用 JSX.IntrinsicElements
而不是 type
来扩展/交叉您的 interface
和在 Props
中的键上定义的特定属性。您还需要在您的函数上使用泛型,以便您可以将它们传递给您的 JSX.IntrinsicElements
类型,这意味着您不能再使用 Props
,因为这发生在访问任何泛型之前。这意味着您需要将 React.FunctionComponent<Props>
添加到您的 children
定义中。
我认为用这个例子更好地解释了很多词:
Props
答案 1 :(得分:1)
您可以传入string
作为标记名,并按原样使用taht,但是您需要正确键入它才能进行类型检查。 tag
应该是JSX.IntrinsicElements
的键。
interface CompProps {
tag: keyof JSX.IntrinsicElements;
}
const MyComponent: React.FunctionComponent<CompProps> = ({
tag = "div",
children
}) => {
const Wrapper = tag as 'div';
return <Wrapper>{children}</Wrapper>;
};
您会注意到以下类型的断言:tag as 'div'
。如果您注释掉类型断言,则需要这样做的原因将变得显而易见。从理论上讲,代码应该起作用。实际上,tsserver将使用超过2GB的内存或RAM挂起。它可能正在尝试检查所有可能的标签,并且由于某种原因挂起了该标签,我认为存在与此相关的GitHub问题(我正在考虑的问题已关闭,打开了一个{{3} }。断言技巧将使其仅检查足够接近的div
。
仅在3.4及更高版本上才出现此性能问题。在3.3中,它无需声明即可工作。
答案 2 :(得分:1)
我有一个类似的问题,我尝试根据传递的“关卡”道具生成动态标题标签。还会生成“属性X在IntrinsicAttributes类型上不存在” 错误。
产生错误的代码如下;
// Heading.tsx
import React, { FunctionComponent, ReactNode } from 'react';
interface PropsType {
level: 1 | 2 | 3 | 5 | 6;
children?: ReactNode;
}
type HeadingTag = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
const HeadingComponent: FunctionComponent = ({
level,
children = null
}: PropsType) => {
const Tag = `h${level}` as HeadingTag;
return (
<Tag>
{children}
</Tag>
);
};
export default HeadingComponent;
// And I used this component all over my codebase like this;
// HomePage.tsx
<Heading level={1}>
This Is A Title
</Heading>
我通过以下方法解决了这个问题:
const HeadingComponent: FunctionComponent = ({
... // removed for brevity
}: PropsType) => {
... // removed for brevity
};
收件人:
const HeadingComponent: FunctionComponent<PropsType> = ({
... // removed for brevity
}) => {
... // removed for brevity
};
答案 3 :(得分:0)