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
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.