JSX元素类型'没有任何结构或呼叫签名;的意思吗?

我写了一些代码:

function renderGreeting(Elem: React.Component<any, any>) {
return <span>Hello, <Elem />!</span>;
}

我得到一个错误:

JSX元素类型Elem没有任何构造或调用签名

这是什么意思?

372646 次浏览

这是构造函数实例之间的混淆。

记住,当你在React中编写一个组件时:

class Greeter extends React.Component<any, any> {
render() {
return <div>Hello, {this.props.whoToGreet}</div>;
}
}

你可以这样用:

return <Greeter whoToGreet='world' />;

这样使用它:

let Greet = new Greeter();
return <Greet whoToGreet='world' />;

在第一个例子中,我们传递了组件的Greeter,即构造函数。这是正确的用法。在第二个例子中,我们传递了一个Greeter实例。这是不正确的,将在运行时失败,并出现类似“对象不是函数”的错误。


这段代码的问题

function renderGreeting(Elem: React.Component<any, any>) {
return <span>Hello, <Elem />!</span>;
}

它期望React.Component实例。你想要的是一个接受一个构造函数React.Component的函数:

function renderGreeting(Elem: new() => React.Component<any, any>) {
return <span>Hello, <Elem />!</span>;
}

或类似的:

function renderGreeting(Elem: typeof React.Component) {
return <span>Hello, <Elem />!</span>;
}

如果你想接受一个组件类作为参数(而不是一个实例),使用React.ComponentClass:

function renderGreeting(Elem: React.ComponentClass<any>) {
return <span>Hello, <Elem />!</span>;
}

当我从JSX转换到TSX时,我们保留了一些库作为js/ JSX,并将其他库转换为ts/ TSX,我几乎总是忘记更改TSX\ ts文件中的js/ JSX导入语句

import * as ComponentName from "ComponentName";

import ComponentName from "ComponentName";

如果从TSX调用旧的JSX (React.createClass)样式组件,则使用

var ComponentName = require("ComponentName")

如果你正在使用material-ui,转到组件的类型定义,它是由TypeScript下划线。你很可能会看到这样的东西

export { default } from './ComponentName';

你有两个选项来解决这个错误:

1.在JSX中使用组件时添加.default:

import ComponentName from './ComponentName'


const Component = () => <ComponentName.default />

2.在导入时,将组件重命名为“default”:

import { default as ComponentName } from './ComponentName'


const Component = () => <ComponentName />

这样你就不需要每次使用该组件时都指定.default

如果你真的不关心道具,那么最宽的可能类型是React.ElementType

这将允许将原生dom元素作为字符串传递。React.ElementType涵盖了所有这些:

renderGreeting('button');
renderGreeting(() => 'Hello, World!');
renderGreeting(class Foo extends React.Component {
render() {
return 'Hello, World!'
}
});

在我的例子中,类型定义中缺少new

some-js-component.d.ts文件:

import * as React from "react";


export default class SomeJSXComponent extends React.Component<any, any> {
new (props: any, context?: any)
}

tsx文件中,我试图导入非类型化组件:

import SomeJSXComponent from 'some-js-component'


const NewComp = ({ asdf }: NewProps) => <SomeJSXComponent withProps={asdf} />

当声明React Class组件时,使用React.ComponentClass而不是React.Component,它将修复ts错误。

在我的例子中,我使用React.ReactNode作为函数组件的类型,而不是React.FC类型。

准确地说,在这个组件中:

export const PropertiesList: React.FC = (props: any) => {
const list:string[] = [
' Consequat Phasellus sollicitudin.',
' Consequat Phasellus sollicitudin.',
'...'
]


return (
<List
header={<ListHeader heading="Properties List" />}
dataSource={list}
renderItem={(listItem, index) =>
<List.Item key={index}> {listItem } </List.Item>
}
/>
)
}

https://github.com/microsoft/TypeScript/issues/28631#issuecomment-472606019我通过这样做来修复它:

const Component = (isFoo ? FooComponent : BarComponent) as React.ElementType

正如@Jthorpe所提到的,ComponentClass只允许ComponentPureComponent,而不允许FunctionComponent

如果你试图传递FunctionComponent, typescript将抛出一个类似于…

Type '(props: myProps) => Element' provides no match for the signature 'new (props: myProps, context?: any): Component<myProps, any, any>'.

然而,通过使用ComponentType而不是ComponentClass,你可以同时使用这两种情况。根据react声明文件,类型定义为…

type ComponentType<P = {}> = ComponentClass<P, any> | FunctionComponent<P>

你可以使用

function renderGreeting(props: {Elem: React.Component<any, any>}) {
return <span>Hello, {props.Elem}!</span>;
}

但是,下面的方法有用吗?

function renderGreeting(Elem: React.ComponentType) {
const propsToPass = {one: 1, two: 2};


return <span>Hello, <Elem {...propsToPass} />!</span>;
}

我在导出组件之前使用< >强类型的断言< / >强来解决这个问题。TypeScript在使用redux 'compose'组合后无法识别,因此我将道具类型分为IParentProps和IProps,并使用 . IParentProps while doing Type Assertions . IParentProps

import { compose } from 'react-redux'
import HOC1 from 'HOCs/HOC1'
import HOC2 from 'HOCs/HOC2'


type IParentProps = {}
type IProps = {}


const Component: React.FC<IProps & IParentProps> = React.memo((props) => {


return <SomeComponent {...props}/>


})


return compose(HOC1,HOC2)(Component) as React.FunctionComponent<IParentProps>

如果你将功能组件作为道具传递给另一个组件,请使用以下命令:

import React from 'react';


type RenderGreetingProps = {
element: React.FunctionComponent<any>
};


function RenderGreeting(props: RenderGreetingProps) {
const {element: Element} = props;


return <span>Hello, <Element />!</span>;
}

这是我搜索错误时的第一个结果,所以我想在我的特定情况下分享解决方案:

我正在使用的库是这样的:

export { default as Arc } from './shapes/Arc';

我导入错误,导致错误:

import Arc from "@visx/shape";

它应该是

import { Arc } from "@visx/shape";

这是语法的问题

下面是一个使用TSX的例子:

const SomeMadeComponent = (props: { someText: string }) => {
const { someText} = props;
return (
<div>
<p>{someText}</p>
</div>
);
};

你可以像使用普通组件一样使用它:

<SomeMadeComponent someText='Place your text here' />

看起来现在有一个特殊的新TypeScript类型来解决这个问题:JSXElementConstructor。如果你让某人将构造函数传递给一个未知的ReactElement,而不是该ReactElement的实例,这是传递的正确类型。

const renderGreeting = (Elem: JSXElementConstructor<any>) => {
return <span>Hello, <Elem />!</span>;
}

这等价于上面选择的正确答案,因为:在JSX中使用<Elem />(也就是用尖括号将大写变量括起来)等价于使用new关键字调用JSX元素的构造函数。

import React from 'react';


function MyComponent (
WrappedComponent: React.FunctionComponent | React.ComponentClass
) {
return (
<Wrapper>
<WrappedComponent />
</Wrapper>
);
}

如果你使用功能组件并传递一个组件作为道具,对我来说,解决方案是改变反应。ReactNode to React。应用的

interface Props{
GraphComp: React.ElementType
}


const GraphCard:React.FC<Props> = (props) => {
const { GraphComp } = props;


return (
<div> <GraphComp /> </div>
)
}