Magnus Jackson
Magnus Jackson

Reputation: 47

Why is the react-map-gl Source not using an object?

I'm trying to add geoJSON into a react-map-gl map using typescript

here is my code

<Source id="my-data" type="geojson" data={data}>
    <Layer {...layerStyles}/>
</Source>

the data variable is a parsed JSON file so it is an object not

the error I get is

 Overload 1 of 2, '(props: SourceProps | Readonly<SourceProps>): Source', gave the following error.
    Type 'object' is not assignable to type 'string | Feature<Geometry, GeoJsonProperties> | FeatureCollection<Geometry, GeoJsonProperties> | undefined'.
      Type '{}' is missing the following properties from type 'FeatureCollection<Geometry, GeoJsonProperties>': type, features
  Overload 2 of 2, '(props: SourceProps, context: any): Source', gave the following error.
    Type 'object' is not assignable to type 'string | Feature<Geometry, GeoJsonProperties> | FeatureCollection<Geometry, GeoJsonProperties> | undefined'.  TS2769

    123 |             onViewportChange={(nextView:typeof viewport) => setViewport(nextView)}>
    124 |                 {/* GeoJSON */}
  > 125 |                 <Source id="my-data" type="geojson" data={data}>
        |                                                     ^
    126 |                  <Layer {...layerStyles}/>
    127 |                 </Source> 
    128 |                 {markers}

And the type of the data props in the Source component is supposed to be an object as you can see from the documentation https://visgl.github.io/react-map-gl/docs/api-reference/source

if you need any more information please ask

Upvotes: 3

Views: 1189

Answers (1)

coagmano
coagmano

Reputation: 5671

Explanation

Let's pull apart this error:

Overload 1 of 2, '(props: SourceProps | Readonly<SourceProps>): Source'
...
Overload 2 of 2, '(props: SourceProps, context: any): Source

At the top level it is showing the two possible ways that you can use the Source component. Assuming you aren't assigning it a context, let's continue looking at Overload 1

Type 'object' is not assignable to type 'string | Feature<Geometry, GeoJsonProperties> | FeatureCollection<Geometry, GeoJsonProperties> | undefined'.

This is telling us that typescript thinks data is an object, but it's expecting it to be specifically a string or a Feature or a FeatureCollection (specifically features of Geometry, GeoJsonProperties)
This tells us that object is not a specific enough type to satisfy the compiler.

Type '{}' is missing the following properties from type 'FeatureCollection<Geometry, GeoJsonProperties>': type, features

Here's it's trying to be helpful by telling us what's missing in the object type. Note that it's reduced your type to an empty object without properties.

Solution

So how do you tell the compiler what data is?
I'm going to assume that you are fetching the data and so typescript is unable to infer it's type automatically

Option 1 - use a type guard to check the incoming data

function isFeatureCollection(data: unknown): data is FeatureCollection<Geometry, GeoJsonProperties> {
    return (
        typeof data === 'object'
        && data !== null
        && 'type' in data
        && 'features' in data
    );
}

Then use the type guard to exit out of the render, and typescript will know that after this check that data will be the type expected by Source

if (!isFeatureCollection(data)) return null;

<Source id="my-data" type="geojson" data={data}>
    <Layer {...layerStyles}/>
</Source>

Option 2 - Cast the type using as to what you expect it to be:

const data = await getData() as FeatureCollection<Geometry, GeoJsonProperties>;

This isn't recommended as you're throwing away Typescript's type guarantee here

Upvotes: 4

Related Questions