0

TypeScript Error:

Type '(location: Location) => Promise' is not assignable to type '(obj: Location) => void'. Types of parameters 'location' and 'obj' are incompatible. Type 'Location' is missing the following properties from type 'Location': state, city

Basically, user types in City and State, a little logic to parse the city and state, then the submitted user input gets raised from form component to top level App component, where there is an API get request. However 'fetchData' is throwing an error when I pass it down as a prop to weather-input (the form component). And 'obj', the argument for 'fetchData' is throwing an error in weather-input (the form component) at the bottom of 'handleSubmit' when it gets called. Not to mention onSumit inside the form tag (not shown below) is also throwing an error.

Question - what's the best way/flow/practice to make these proper 'types' and remove the errors?

  • weather-input.tsx file

      import React, { FormEvent, useState } from 'react';
      import { useHistory } from 'react-router-dom';
      import '../styling/weather-input.css';
    
      type WeatherInputProps = {
        fetchData: (obj: Location) => void;
      }
    
      export const WeatherInput: React.FC<WeatherInputProps> = ({fetchData}) => {
        const [input, setInput] = useState('');
        const history = useHistory();
    
        // FormEvent (event handle type), <HTMLButtonElement> restrict to specific element (generic)
        const handleSubmit = (e: FormEvent<HTMLButtonElement>): void => {
          e.preventDefault();
          console.log(input)
          let words: string | string[] = input.replace(',', '')
          words = words.split(' ').reverse();
          let obj = {
            state: words[0],
            city: words.slice(1).reverse().join(' ')
          }
    
          if (obj.state.length > 2 || obj.state.length < 2) {
            alert('use to letters for state')
          } else {
            fetchData(obj)
          }
          history.push('/forecast');
        }
    
  • App.tsx file

      import React, { useState } from 'react';
      import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
      import axios from 'axios';
      import '../styling/App.css';
      import { WeatherInput } from './weather-input';
      import { Week } from './week';
      import { Day } from './day';
    
      type Location = {
        state: string;
        city: string;
      }
    
      const App: React.FC = () => {
        const [day, setDay] = useState<string[]>([]);
        const [items, setItems] = useState<string[]>([]);
    
    
        // openweathermap 
        const fetchData = async (location: Location) => {
          const API_URL = 'https://api.openweathermap.org/data/2.5/forecast';
          const API_KEY = process.env.REACT_APP_API_KEY;
    
          const options = `${API_URL}?q=${location.city},${location.state},US&appid=${API_KEY}`
          try {
            const {data} = await axios.get(options);
            setItems(data)
          } catch (err) {
            console.error(err)
            alert('Please check spelling of city and state.')
          };
        };
    
        const hanldeSingleDay = (e: any, day: any) => {
          let item: Array<string> = [day]; 
          setDay(item)
        }
    
        return (
          <Router>
            <div className="App">
              <WeatherInput fetchData={fetchData}/>
    
5
  • That sort of error usually means that the function isn't expecting an async function. Async functions return promises transparently. So either change your weather input function to work without async or change the definition of weatherinput to expect an async result. Commented Mar 17, 2021 at 13:33
  • so something like this?.... const fetchData = async (location: Location): Promise<Array<Location>> ....(but it still gives me an error, A function whose declared type is neither 'void' nor 'any' must return a value.) Commented Mar 17, 2021 at 16:16
  • It wants you to return the Array<Location> you are promising. Obviously with async you would have some await calls but at the end you should return either an Empty Array or the Array of Locations Commented Mar 17, 2021 at 16:23
  • I'm not following... looking up Async/await in TypeScript, it should start with Promise<Array<(type)>>, would you mind posting an example of what you mean? because it sounds like you're saying something different to me. Commented Mar 17, 2021 at 16:37
  • 1
    There are two types of Async functions generally. Either it returns a value or it doesn't. I'm a bit rusty in typescript but essentially if you specify a type between the < > in Promise<TYPE> then you should have a statement that returns either an instance of that TYPE, or another promise that returns that type. Commented Mar 17, 2021 at 17:58

1 Answer 1

1

Looking at your code you should be able to change the definition of WeatherInputProps to expect an asynchronous function like so:

type WeatherInputProps = {
    fetchData: (obj: Location) => Promise<void>;
}

If you want to return a value from the async function you would change Promise<void> to Promise<RETURNTYPE> where RETURNTYPE is any type you want as the result of an asynchronous function.

Then you would have to ensure you return a value of that type in the async function like so:

const fetchData = async (loc:Location) => {
      await doAsynchronousWork();
      return new RETURNTYPE();
   };
Sign up to request clarification or add additional context in comments.

4 Comments

thank you, this makes more sense now. would you have any suggestions for, const hanldeSingleDay = (e: any, day: any) => ... other than type any? I'm getting an error now that the other bit is cleaned up... Type '{ hanldeSingleDay: (e: any, day: any) => void; items: string[]; history: History<unknown>; location: Location<unknown>; match:
If you are working with React, most likely that e is an event, which probably has a type you can specify, like Event , or Event | null if you don't know if it exists, and I would assume day is either a number or a Date object
the second arg after the event is 'item'. That is an object, for one day. Which holds additional objects inside, date, main, weather, clouds, etc. All of those are objects within the object. some I use, some I don't. Does this mean I need to create types for each object? And then each object within the object?
You can define a wrapper item with all the different types: interface ItemWrapper { data: Item| Date| Weather| Clouds | null } Ideally you do create an object for each, to help detect type errors and such

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.