Adding Typescript interfaces for deeply nested object returned by API

734 Views Asked by At

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!

1

There are 1 best solutions below

2
On BEST ANSWER

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

// ...