5

I have a React Class Component with two properties in state. I'm currently getting a TypeScript error when trying to dynamically setState that says Property 'input' does not exist on type 'Readonly<{}>'.

I'm newer to TypeScript and I haven't yet had to tackle the problem of adding Type definitions to a Class Component before. I've been working mostly with functional components and hooks so this issue hasn't come up for me.

I defined the type for my App State and then passed it into the component, but I'm still getting the original error as well as a new error where I define state that says 'AppState' only refers to a type, but is being used as a value here.

I've been searching around for a solution but haven't been able to solve this.

My original component

type AppState = {
  input: string;
  imageUrl: string;
}

class App extends Component<AppState> {
  constructor(props: any) {
    super(props);
    // Error: 'AppState' only refers to a type, but is being used as a value here.
    this.state: AppState = {
      input: "",
      imageUrl: "",
    };
  }

  onInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    this.setState({ input: e.target.value });
  };

  onButtonSubmit = () => {
    // Error: Property 'input' does not exist on type 'Readonly<{}>'.
    this.setState({ imageUrl: this.state.input });
    clarifaiApp.models
      .predict(
        Clarifai.COLOR_MODEL,
        // URL
        "https://samples.clarifai.com/metro-north.jpg"
      )
      .then(
        function (response: any) {
          console.log("This is your response: ", response);
        },
        function (err: Error) {
          console.log("There was an error: ", err);
        }
      );
  };

  render() {
    return (
      <Container>
        <ImageLinkForm
          onInputChange={this.onInputChange}
          onButtonSubmit={this.onButtonSubmit}
        />
        {/* Error: Property 'imageUrl' does not exist on type 'Readonly<{}>'. */}
        <FaceRecognition imageUrl={this.state.imageUrl} />
      </Container>
    );
  }
}

export default App;

3 Answers 3

6

If you want to initialize the state inside the constructor of a class component you have to provide two type argument to the React.Component generic. The first argument is meant to provide type information about the props and the second argument is meant to provide the type information about the state. For example,

interface AppProps {
  // props 
}

type AppState = {
  input: string;
  imageUrl: string;
}

class App extends Component<AppProps,AppState> {
  // you don't need any more explicit type annotation here
  constructor(props) {
    super(props);
    this.state = {
      input: "",
      imageUrl: "",
    };
  }

  // rest of the app logic
}

This would not have caused any issue if you would have initialized the state in the state field outside the constructor.

type AppState = {
  input: string;
  imageUrl: string;
}

class App extends Component {
  state: AppState = {
    input: "",
    imageUrl: ""
  }


  // rest of the app logic
}

In the second case, you can choose to provide a type argument for the props if the component is expecting some props. For example,

interface AppProps {
  // type info for props
}

type AppState = {
  input: string;
  imageUrl: string;
}

class App extends Component<AppProps> {
   state: AppState = {
      input: "",
      imageUrl: ""
   }

   // rest of the app logic
}
Sign up to request clarification or add additional context in comments.

Comments

2

For React class components, you will generally need to supply the generic prop parameters with the types for the props and state respectively.

class App extends Component<AppProps, AppState> {

  ...

}

For your case, since there are no props for the App component, you will only need to do this:

class App extends Component<{}, AppState> {
  constructor(props: {}) {
    super(props);
    this.state: AppState = {
      input: "",
      imageUrl: "",
    };
  }    
  ...

}

1 Comment

That did it! Thank you! Strangely enough I was still getting an error at this.state: AppState = {...} so I removed AppState to change it to this this.state = {...} and it works!
1

if you want to try it with the hooks then you can do this.

import React, { useState } from "react";

interface AppState {
  input: string;
  imageUrl: string;
}

const App: React.FC<AppState> = () => {
  const [input, setInput] = useState("");
  const [imageUrl, setImageUrl] = useState("");

  const handleOnButtonSubmit = () => {
    setImageUrl(input);
    clarifaiApp.models
      .predict(
        Clarifai.COLOR_MODEL,
        // URL
        "https://samples.clarifai.com/metro-north.jpg"
      )
      .then(
        function(response: any) {
          console.log("This is your response: ", response);
        },
        function(err: Error) {
          console.log("There was an error: ", err);
        }
      );
  };

  return (
    <Container>
      <ImageLinkForm
        onInputChange={(e: any) => setInput(e.target.value)}
        onButtonSubmit={handleOnButtonSubmit}
      />
      <FaceRecognition imageUrl={imageUrl} />
    </Container>
  );
};

export default App;

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.