ReactJS和Typescript:在此处被用作类型,但它是一个值的引用(TS2749)

246

我正在编写一个使用Typescript和Material-ui的ReactJS类,保存于.tsx文件中。在我的自定义组件中,我想创建一个对我使用的组件的引用。

export class MyTextField extends React.Component<MyProps, MyState> {
  private refTextField: React.RefObject<TextField>;
  constructor(props: MyProps) {
    super(props);
    this.refTextField = React.createRef();
  }

  render(): JSX.Element {
    const { id, label, value: defaultValue } = this.props;
    const { value } = this.state;
    const element = (
      <TextField ref={this.refTextField} id={id} label={label} defaultValue={defaultValue} value={value} />
    );

    return element;
  }
}

编译时,在我的引用声明中出现错误:

'TextField' 指的是一个值,但在此处被用作类型。TS2749

我尝试在声明中加入 "typeof TextField",但在渲染时 valuing ref 属性时又出现了另一条信息:

类型 'RefObject<(props: TextFieldProps) => Element>' 不可分配给类型 '((instance: HTMLDivElement | null) => void) | RefObject | null | undefined'。类型 'RefObject<(props: TextFieldProps) => Element>' 不可分配给类型 'RefObject'。 类型 '(props: TextFieldProps) => Element' 中缺少以下属性:align、addEventListener、removeEventListener、accessKey 和其他 238 个属性。TS2322

有什么想法吗? 非常感谢。


可能相关 https://dev59.com/3FYN5IYBdhLWcg3w570u - Michael Freidgeim
我在我的情况下遇到了同样的问题,这个方法有效!https://dev59.com/LFIG5IYBdhLWcg3w9W3d#65332778 - Diego Arrieta
12个回答

919

请确保您正在编辑的是 .tsx 文件而不是 .ts 文件。


有一个类似的问题,答案相同 https://dev59.com/D1MH5IYBdhLWcg3wxinj - Michael Freidgeim
3
谢谢 :D 我既感到宽慰,因为解决方案真的很简单,又感到恼怒,因为解决方案真的很简单! - Gavin
3
我已经努力了半个小时来确定问题,谢谢。 - Amr
1
兄弟,你刚刚救了我一命。 - Sandip Satyal

261

.ts 文件更改为 .tsx

在我的情况下它有效

81

我以前在代码中多次遇到这个问题,但是直到今天才找出了原因。

TL;DR: 在你的情况下,你只需要写InstanceType<typeof TextField>而不是TextField

当你在TypeScript中创建一个类时,该类的名称既指代实例类型,也指代JavaScript类值。如果你将那个类作为一种类型引用,TypeScript会自动检测到它是该类的实例类型。如果你在运行时引用该类,它就会按照JavaScript的含义使用它。到目前为止,一切都很好。

class MyClass {}
let abc: MyClass; // ts recognizes as instance type
abc = new MyClass(); // completely fine, used here as the javascript value

然而,真正的问题出现在你以 动态 方式从模块中导出类时。当你以某些方式导出类时,TypeScript 只能导出类的 Javascript 值,而无法导出类型。因此,如果你在另一个模块中导入它并尝试将其引用为类型,则会收到 TS2749 的错误提示。

let intervariable = class MyClass{}
export const MyClass = intervariable; // TypeScript does not export type here.
import {MyClass} from './myclass';

let abc: MyClass; // TypeScript error TS2749
当出现这种情况,特别是如果它不在你的控制之下时,我获取实例类型的解决方案就是简单地使用 InstanceType 和 typeof:
import {MyClass} from './myclass';

let abc: InstanceType<typeof MyClass>; // no error
// the rest...

typeof 运算符可获取类值的类构造函数类型,而 InstanceType 泛型可获取所需实例类型。


2
我花了太多时间研究这个问题...谢谢。另一个观点是Jest管道与生产/开发管道完全不同... - Ray Foss
1
是的,由于某种原因,在TS文档中没有得到很好的解释。我的团队在使用一些非TS库的TS-React项目上遇到了这个问题多次。我花了一段时间才弄清楚这个问题。 - Mehmet Efe Akça

10
这本来应该是一条评论,但我的声望不够。 我想分享一个使用被接受的答案提供的解决方案的简洁方法,就是声明一个同名的类型别名(不会造成冲突)。然后您可以像通常使用实例类型一样使用该类型。
补充例子:
import {MyClass} from './myclass';

type MyClass = InstanceType<typeof MyClass>;

let abc: MyClass; // still no error!

编辑:一种类型导入似乎也可以,而且看起来更简洁:

import type {MyClass} from './myclass';

使用 import type 的注意点是,您无法将其用作值(例如构造 new MyClass(...))。

4

对我来说,也出现了这个错误,将导入语句从:

import mytype from '../path/file'

to

import { mytype } from '../path/file'

问题已解决。


这是最近引入的东西吗?因为这个问题突然出现在我的项目中,之前并没有这个问题。 - undefined

4
对我来说,错误是由于文件名扩展名错误导致的-使用了.ts而不是.js。

有时候TS就是你想要的。Typescript不是文件名中的拼写错误。 - SeanMC
这解决了我的问题,谢谢! - Hari Prasad
这解决了我的问题,谢谢! - undefined
哇,这就是我的问题啊。我的扩展名是.ts,但我将JSX表达式作为对象属性(例如{icon: <Pen/>})使用了。将文件名更改为.tsx可以解决这个错误。 - Muhammad Mubashirullah Durrani
我的文件已经被命名为 tsx,我不得不将其重新命名为 ts,然后再改回 tsx,神奇地一切都恢复正常了 :D - Nishant Sah

2

在我的情况下,使用关键字"typeof"作为数据类型前缀解决了问题。

let variable: typeof <data type> = ...

例如:

 let packs: typeof Card[] = await Card.find({ category: cat, rarity: rar });

2

另一个需要检查的事情是你所执行的导入是否被{}包围。出现此消息的原因之一是因为未被{}包围的任何内容都被视为文件的默认导出。


0

我不得不将 heroes: HEROES 改为 heroes = HEROES

在构建 Angular 应用程序时,我遇到了同样的错误:x 引用一个值,但在此处被用作类型。你是指 'typeof x' 吗? 我花了两个小时搜索修复方法,多次阅读了这篇 Stack Overflow 帖子。这只是我的一个简单错误。


0

对于未来的观众,错误也可能是由于您首先没有导入类而引起的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接