javascriptreactjstypescriptreverse-geocoding

Typescript: Defining an interface for an array of objects from a google JSON?


I'm looping over my data that's returned from a Google API to receive the city and state, but I keep receiving this error

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Geocode'.
  No index signature with a parameter of type 'string' was found on type 'Geocode'.

Which appears when I try to loop the data:

data[key].address_components.forEach

So far I looked at "Indexing objects" and using a interface, but I'm still not sure how to use them

Here is my code so far:

type Geocode = {
  data?: google.maps.GeocoderResult | null;
  error: string | null;
};

export interface CityState {
  city?: string;
  state?: string;
}

export const parseForCityAndState = (data: Geocode): CityState => {
  const cityState = {
    city: '',
    state: '',
  };

  if (data) {
    for (const key in data) {
      data[key].address_components.forEach((ele: {types: string | string[]; short_name: string}) => {
        if (ele.types.includes('sublocality') || ele.types.includes('locality')) {
          cityState.city = ele.short_name;
        }

        if (ele.types.includes('administrative_area_level_1')) {
          cityState.state = ele.short_name;
        }
      });
    }
  }

  return cityState;
};

Here is how the google JSON returns the data:

{
    "data": {
        "address_components": [
            {
                "long_name": "254",
                "short_name": "254",
                "types": [
                    "street_number"
                ]
            },
            {
                "long_name": "Broadway",
                "short_name": "Broadway",
                "types": [
                    "route"
                ]
            },
            {
                "long_name": "Manhattan",
                "short_name": "Manhattan",
                "types": [
                    "political",
                    "sublocality",
                    "sublocality_level_1"
                ]
            },
            {
                "long_name": "New York",
                "short_name": "New York",
                "types": [
                    "locality",
                    "political"
                ]
            },
            {
                "long_name": "New York County",
                "short_name": "New York County",
                "types": [
                    "administrative_area_level_2",
                    "political"
                ]
            },
            {
                "long_name": "New York",
                "short_name": "NY",
                "types": [
                    "administrative_area_level_1",
                    "political"
                ]
            },
            {
                "long_name": "United States",
                "short_name": "US",
                "types": [
                    "country",
                    "political"
                ]
            },
            {
                "long_name": "10007",
                "short_name": "10007",
                "types": [
                    "postal_code"
                ]
            }
        ],
        "formatted_address": "254 Broadway, New York, NY 10007, USA",
        "geometry": {
            "location": {
                "lat": 40.7129032,
                "lng": -74.0063033
            },
            "location_type": "ROOFTOP",
            "viewport": {
                "south": 40.7115542197085,
                "west": -74.0076522802915,
                "north": 40.7142521802915,
                "east": -74.00495431970849
            }
        },
        "place_id": "ChIJzwO0CyJawokRoSRPRCTv9II",
        "plus_code": {
            "compound_code": "PX7V+5F New York, NY, USA",
            "global_code": "87G7PX7V+5F"
        },
        "types": [
            "street_address"
        ]
    },
    "error": null
} 

Solution

  • This is a problem caused by the fact that TSC does not know whether that object can be indexed by a string. This is normally solved by typing whatever you're indexing as Record<string, T>.

    const data: Record<string, any> = ...;
    data[key].address_components.forEach
    

    You can also cast it:

    (data as Record<string, any>)[key]