我写了一些代码:
function renderGreeting(Elem: React.Component<any, any>) {
return <span>Hello, <Elem />!</span>;
}
我收到一个错误:
JSX 元素类型
没有任何构造或调用签名Elem
什么意思?
这是构造函数和实例之间的混淆。
请记住,当您在 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
的 instance。这是不正确的,并且会在运行时失败并出现类似“对象不是函数”的错误。
这段代码的问题
function renderGreeting(Elem: React.Component<any, any>) {
return <span>Hello, <Elem />!</span>;
}
是它期待React.Component
的
instance。你想要的是一个函数,它需要 a constructor for
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>;
}
以下对我有用:https://github.com/microsoft/TypeScript/issues/28631#issuecomment-472606019我通过这样做来修复它:
const Component = (isFoo ? FooComponent : BarComponent) as React.ElementType
当我从 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")
如果您将功能组件作为道具传递给另一个组件,请使用以下内容:
import React from 'react';
type RenderGreetingProps = {
element: React.FunctionComponent<any>
};
function RenderGreeting(props: RenderGreetingProps) {
const {element: Element} = props;
return <span>Hello, <Element />!</span>;
}
如果您正在使用 Functional Component 并将组件作为 prop 传递,对我来说,解决方案是将 React.ReactNode 更改为 React.ElementType
interface Props{
GraphComp: React.ElementType
}
const GraphCard:React.FC<Props> = (props) => {
const { GraphComp } = props;
return (
<div> <GraphComp /> </div>
)
}
如果你真的不关心道具那么最广泛的可能类型是
React.ElementType
.
这将允许将原生 dom 元素作为字符串传递。
React.ElementType
涵盖所有这些:
renderGreeting('button');
renderGreeting(() => 'Hello, World!');
renderGreeting(class Foo extends React.Component {
render() {
return 'Hello, World!'
}
});
看起来现在有一个特殊的新 TypeScript 类型可以解决这个问题的需要:
JSXElementConstructor
。如果您让某人将构造函数传递给未知的 ReactElement 而不是该 ReactElement 的实例,这是要传递的正确类型。
const renderGreeting = (Elem: JSXElementConstructor<any>) => {
return <span>Hello, <Elem />!</span>;
}
这等同于上面选择的正确答案,因为:在 JSX 中使用
<Elem />
(又名用尖括号包裹大写变量)等同于使用 new
关键字调用 JSX 元素的构造函数。
如果您使用的是material-ui,请转到组件的类型定义,它由 TypeScript 加下划线。你很可能会看到这样的东西
export { default } from './ComponentName';
您有 2 个选项来解决错误:
1.在JSX中使用组件时添加
.default
:
import ComponentName from './ComponentName'
const Component = () => <ComponentName.default />
2.导入时重命名导出为“默认”的组件:
import { default as ComponentName } from './ComponentName'
const Component = () => <ComponentName />
这样就不需要每次使用组件时都指定
.default
正如@Jthorpe 所暗示的,
ComponentClass
只允许 Component
或 PureComponent
但不允许 FunctionComponent
.
如果你试图传递一个
FunctionComponent
,打字稿会抛出一个类似于...的错误
Type '(props: myProps) => Element' provides no match for the signature 'new (props: myProps, context?: any): Component<myProps, any, any>'.
但是,通过使用
ComponentType
而不是 ComponentClass
,您可以同时考虑这两种情况。根据反应声明文件,类型定义为...
type ComponentType<P = {}> = ComponentClass<P, any> | FunctionComponent<P>
这是我搜索错误时的第一个结果,所以我想在我的特定情况下分享解决方案:
我正在使用的图书馆看起来像这样:
export { default as Arc } from './shapes/Arc';
我导入错误,导致错误:
import Arc from "@visx/shape";
应该是
import { Arc } from "@visx/shape";
在声明 React 类组件时,使用
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>
}
/>
)
}
import React from 'react';
function MyComponent (
WrappedComponent: React.FunctionComponent | React.ComponentClass
) {
return (
<Wrapper>
<WrappedComponent />
</Wrapper>
);
}
就我而言,我在类型定义中缺少
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} />
你可以使用
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>;
}
我在导出组件之前使用 Type Assertions 解决了这个问题。 TypeScript 在使用 redux 'compose' 组合后无法识别因此我将道具类型分为 IParentProps 和 IProps 并在进行类型断言时使用 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>
这里有一个使用 TSX 的例子:
const SomeMadeComponent = (props: { someText: string }) => {
const { someText} = props;
return (
<div>
<p>{someText}</p>
</div>
);
};
你像普通组件一样使用它:
<SomeMadeComponent someText='Place your text here' />