Last updated: Jan 15, 2023
Reading timeยท3 min

The React.js error "Property does not exist on type 'Readonly<{}>'" occurs when we try to access the props or state of a class component that we haven't typed.
To solve the error, use the generic on the React.Component class to type the
props or state objects of the class.

Here is an example of how the error occurs.
import React from 'react'; class App extends React.Component { constructor(props: any) { super(props); this.state = {value: ''}; // ๐๏ธ uses state but we haven't typed it } handleChange = (event: any) => { this.setState({value: event.target.value}); }; render() { return ( <div> <form> {/* โ๏ธ Error: Property 'value' does not exist on type 'Readonly<{}>'.ts(2339) */} <input onChange={this.handleChange} type="text" value={this.state.value} /> <button type="submit">Submit</button> </form> </div> ); } } export default App;
Notice that our class component has a value property in its state object.
The same is the case for the props object - trying to access a property on the
props object would cause an error if we don't explicitly type it.
To solve the error, use the generic of the React.Component class as
React.Component<PropsObject, StateObject>.
import React from 'react'; // ๐๏ธ we set the props to empty object and set the state to {value: string} class App extends React.Component<{}, {value: string}> { constructor(props: any) { super(props); this.state = {value: ''}; } handleChange = (event: any) => { this.setState({value: event.target.value}); }; render() { return ( <div> <form> <input onChange={this.handleChange} type="text" // โ Everything works now value={this.state.value} /> <button type="submit">Submit</button> </form> </div> ); } } export default App;
We typed the value property on the state object in the class, so we are now
able to access it as this.state.value.
any typeIf you don't know how to type the props or state objects and want to disable
type checking, use the any type.
import React from 'react'; // ๐๏ธ type checking disabled for props and state class App extends React.Component<any, any> { constructor(props: any) { super(props); this.state = {value: ''}; } handleChange = (event: any) => { this.setState({value: event.target.value}); }; render() { return ( <div> <form> <input onChange={this.handleChange} type="text" value={this.state.value} /> <button type="submit">Submit</button> </form> </div> ); } } export default App;
We used the any type when typing the props and state objects, which
effectively turns off type checking.
this.props and this.state objects without getting a type-checking error.Here is an example of a class that also explicitly types the props object.
import React from 'react'; // ๐๏ธ type props as {name: string}, and state as {value: string} class App extends React.Component<{name: string}, {value: string}> { constructor(props: any) { super(props); this.state = {value: ''}; } handleChange = (event: any) => { this.setState({value: event.target.value}); }; render() { return ( <div> <form> <input onChange={this.handleChange} type="text" value={this.state.value} /> <button type="submit">Submit</button> </form> <h1>{this.props.name}</h1> </div> ); } } export default App;
We explicitly typed the props object of the App component to have a name
property of type string. Now when you use the component, you would have to
provide the name prop, e.g. <App name="James Doe" />.
You can learn more about using TypeScript with React by checking out the following tutorials: