Search code examples
reactjstypescripttypestypescript2.0object-type

Adding Typescript interfaces for deeply nested object returned by API


I'm new to Typescript and trying to add types to a simple app. I'm stuck on an error with a deeply nested object.

export default function AnimalAdoptionCosts() {
  const [currencyQuote, setCurrencyQuote] = useState({});
  const [userInput, setUserInput] = useState("");

    const fetchAdoptionRates = async (userInput: string) => {
    const response = await fetch(
      `http://localhost:5000/v1/adoptionRate?animal=${userInput}`
    );
    console.log(response);
    const body = await response.json();
    if (response.status !== 200) throw Error(body.message);
    setCurrencyQuote(body);
  };


  const handleSubmit = async (userInput: string) => {
    await fetchExchangeRates();
    const costToAdopt = currencyQuote?.data.quote.USD.price;
  };

  return (
    <>
      <form onSubmit={() => handleSubmit(userInput)}>
        <label htmlFor="animal">Enter an animal type</label>
        <input
          value={userInput}
          id="userinput"
          placeholder="ex: Pomeranian"
          onChange={(event) => setUserInput(event.target.value)}
        />
        <input type="submit" value="Submit" />
       {costToAdopt}
      </form>
    </>

The Typescript error is: [1] Property 'data' does not exist on type '{}'. TS2339 for this line: const costToAdopt = currencyQuote?.data.quote.USD.price;

I get that this is because useState sets the default as an empty object, but it feels weird to do

interface CurrencyQuote {
 data: {
    quote:  {
       USD:   {
         price: number
       }
    }
}

is this what I should be doing? I found this example playground to work off of, but then I would make 4 different interfaces, which seems overcomplicated.

Also if you're a TS whiz, I'd love help figuring out how to type out the other important info in my function. Thank you!


Solution

  • If you only need costToAdopt, I'd recommend just storing that value alone in your useState as a number. That makes the typing a lot easier and you can avoid all the nesting.

    const [costToAdopt, setCostToAdopt] = useState<number>(0);
    
    // ...
    
    const fetchAdoptionRates = async (userInput: string) => {
      const response = await fetch(
        `http://localhost:5000/v1/adoptionRate?animal=${userInput}`
      );
      console.log(response);
      const body = await response.json();
      if (response.status !== 200) throw Error(body.message);
      setCostToAdopt(body?.data?.quote?.USD?.price || 0);
    };
    
    // ...