ReactJS and Typescript : refers to a value, but is being used as a type here (TS2749)
Asked Answered
D

13

270

I'm coding a ReactJS class with Typescript and Material-ui, in a .tsx file. In one of my custom components, I want to create a reference to one of the components that I use in my custom component.

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;
  }
}

During compilation, I get an error on the declaration of my reference:

'TextField' refers to a value, but is being used as a type here. TS2749

I tried to put "typeof TextField" in my declaration, but I have another message, when valuing the ref property in my render :

Type 'RefObject<(props: TextFieldProps) => Element>' is not assignable to type '((instance: HTMLDivElement | null) => void) | RefObject | null | undefined'. Type 'RefObject<(props: TextFieldProps) => Element>' is not assignable to type 'RefObject'. Type '(props: TextFieldProps) => Element' is missing the following properties from type 'HTMLDivElement': align, addEventListener, removeEventListener, accessKey, and 238 more. TS2322

Any ideas ? thank you so much

Doff answered 28/5, 2020 at 7:39 Comment(2)
May be Related #46703864Northerner
i ran the same problem in my case this worked! https://mcmap.net/q/108591/-reactjs-and-typescript-refers-to-a-value-but-is-being-used-as-a-type-here-ts2749Premillennial
H
90

So I ran into this problem multiple times in my code before but only figured out the reason this was happening today.

TL;DR: In your case you just have to write InstanceType<typeof TextField> instead of TextField.

When you create a class in TypeScript, the name of that class refers to both the instance type and the Javascript class value. If you reference that class as a type, TypeScript detects that automatically as the instance type of that class. And if you reference that class in the runtime, it just uses it as in the Javascript meaning. And it's all good and dandy till now.

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

However, the real problem is when you export the class from a module dynamically. When you export the class in some ways, TypeScript can only export the Javascript value of the class and does not export the type. So if you import it in another module and try to reference it as a type, you will get 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

When this happens, especially if it is out of your control, my solution to get the instance type was simply to use InstanceType and typeof:

import {MyClass} from './myclass';

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

The typeof operator gets you the class constructor type for a class value, and the InstanceType generic gets you the instance type that you want.

Hydroponics answered 28/8, 2020 at 18:39 Comment(2)
I spent way too much time looking into this... Thank you. Another insight is that the Jest pipeline is completely different from the production/dev pipeline...Amazed
Yeah, for some reason, this is not properly explained in the TS docs. My team has encountered this issue multiple times while working on a TS-React project with some non-TS libraries. Took me a while to figure this out.Zimmer
C
1009

Make sure you're on a .tsx file and not a .ts file

Carner answered 11/10, 2020 at 12:40 Comment(2)
There is a similar question with the same answer #58342045Northerner
Even 4 years later you saved me!Moray
M
280

change .ts file to .tsx

in my case it works
Micrometeorology answered 16/12, 2020 at 23:50 Comment(0)
H
90

So I ran into this problem multiple times in my code before but only figured out the reason this was happening today.

TL;DR: In your case you just have to write InstanceType<typeof TextField> instead of TextField.

When you create a class in TypeScript, the name of that class refers to both the instance type and the Javascript class value. If you reference that class as a type, TypeScript detects that automatically as the instance type of that class. And if you reference that class in the runtime, it just uses it as in the Javascript meaning. And it's all good and dandy till now.

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

However, the real problem is when you export the class from a module dynamically. When you export the class in some ways, TypeScript can only export the Javascript value of the class and does not export the type. So if you import it in another module and try to reference it as a type, you will get 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

When this happens, especially if it is out of your control, my solution to get the instance type was simply to use InstanceType and typeof:

import {MyClass} from './myclass';

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

The typeof operator gets you the class constructor type for a class value, and the InstanceType generic gets you the instance type that you want.

Hydroponics answered 28/8, 2020 at 18:39 Comment(2)
I spent way too much time looking into this... Thank you. Another insight is that the Jest pipeline is completely different from the production/dev pipeline...Amazed
Yeah, for some reason, this is not properly explained in the TS docs. My team has encountered this issue multiple times while working on a TS-React project with some non-TS libraries. Took me a while to figure this out.Zimmer
O
11

This should have been a comment, but I don't have enough reputation. I wanted to share that a clean way to use the solution recommended by the accepted answer is to declare a type alias with the same name (it does not cause a conflict). Then you can use your type in the same way you would normally use an instance type.

Adding to the example:

import {MyClass} from './myclass';

type MyClass = InstanceType<typeof MyClass>;

let abc: MyClass; // still no error!

Edit: A type import seems to work as well and it looks cleaner:

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

The caveat with the import type approach is that you cannot use it as a value (e.g. constructing new MyClass(...)).

Overweening answered 8/7, 2021 at 12:54 Comment(0)
C
6

First change the file type .tsx

then press F1 and type "Reload Window"

:)

Corbet answered 1/12, 2023 at 7:47 Comment(2)
@kofeigen his answer was actually correct and provided a ton of help. Why would it be removed by Stack Overflow? This is nonsense.Irretrievable
@Irretrievable Sorry. My mistake. I've removed my comment. Thanks.Malatya
G
4

For me, the error occurred due to the wrong filename extension - using .ts instead of .js

Gardener answered 6/7, 2022 at 10:30 Comment(3)
sometimes TS is what you want. Typescript isn't a typo in a filename.Siddons
OMG, this was my issue. My extension was .ts but I had jsx expression as an object property (icon instances eg {icon: <Pen/>}). Changing filename to tsx fixed this error.Inessive
my file was already named tsx, i had to rename it to ts and then back to tsx and magically everythign fell into place :DDismay
C
4

For me, the error occurred as well and changing the import from:

import mytype from '../path/file'

to

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

resolved the issue.

Company answered 6/9, 2022 at 12:0 Comment(1)
Is it something that is was recently introduced? Because this issue appeared out of the blue in my project and was not a problem beforeVela
S
2

Another thing to check is if the import you're performing is surrounded by {} . One of the reasons this message appears is because anything that is not surrounded by {} is treated as the default export for a file.

Sayette answered 14/12, 2021 at 18:6 Comment(0)
S
2

Prepending the datatype with the keyword "typeof" did the trick for me.

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

For example:

 let packs: typeof Card[] = await Card.find({ category: cat, rarity: rar });
Sprain answered 22/11, 2022 at 10:37 Comment(0)
S
0

For future viewers, the error might also be caused by you not importing the class in the first place.

Stampede answered 27/6, 2021 at 12:39 Comment(0)
C
0

I had to change heroes: HEROES to heroes = HEROES.

I ran across this same x refers to a value, but is being used as a type here. Did you mean 'typeof x'? error building an Angular app. I spent two hours searching for the fix, reading this Stack Overflow post several times. Simple mistake on my part.

Chummy answered 12/11, 2021 at 15:8 Comment(0)
H
0

It can also happen if the TypeScript type is

const User = {
    email: string
}

instead of

interface User {
    email?: string | undefined
}
Hueston answered 1/10, 2023 at 6:14 Comment(0)
E
0

I was missing brackets -- had

const example: [status in ExampleOne]: ExampleTwo = {

and changed it to

const example: { [status in ExampleOne]: ExampleTwo } = {

Espy answered 14/12, 2023 at 3:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.