Typescript and React: Type 'Element' is not assignable to type 'FunctionComponent<{}>'

6.7k views Asked by At

I am converting a react project over to typescript and I'm having an issue. The scenario is this: I have a series of Chapter components. Each Chapter is associated with its own unique data and unique Map component. Each Chapter renders a ChapterTemplate, and passes the Map as a prop:

// Chapter.tsx

import model from './modal';
import Map from './Map';

type ChapterProps = {
    data: ModelSchema;
    map: ReactElement;
};

const Chapter: FunctionComponent<ChapterProps> = () => {
    return <ChapterTemplate data={model} map={<Map />} />;
};

This is giving me the following ts error at map={<Map />}:

Type 'Element' is not assignable to type 'FunctionComponent<{}>'. Type 'Element' provides no match for the signature '(props: { children?: ReactNode; }, context?: any): ReactElement<any, any>'.

I am not sure how to type this prop based on how I am using it.

For some context, here is what a <Map /> component should look like:

type MapProps = {
    metadata?: {
        name: string;
        theme: string;
    };
    mapState?: {
        center: number[];
        zoom: number;
        basemap: any;
        layers: any[];
    };
};

const Map = ({
    metadata,
    mapState: { basemap, layers, zoom, center },
}: MapProps) => {
  
  // ... custom logic here for each chapter ...

}

Ultimately it is the ChapterTemplate component that organizes the data, manages the state, and passes certain pieces of data to the Map as props using React.cloneElement:

const Chapter = ({
  map,
  data: { pages, metadata },
}: ChapterProps) => {
  const [currentPage, setCurrentPage] = useState(0);
  const Map = map;

  return (
    <Wrapper>
      // Some other stuff in here
      <MapContainer>
        {cloneElement(map as null, {
          mapState: pages[currentPage].mapState,
          metadata,
        })}
      </MapContainer>
    </Wrapper>
  );
};

I read the question How to assign the correct typing to React.cloneElement when giving properties to children?, which is where I got the idea to use ReactElement as the type for my map prop. I tried using map as ReactElement<any> in my cloneElement call but it was still giving me errors, so I gave up and put in as null for now. But I am still getting this error. For the record, the code does work and compile - react is happy with my syntax and organization. But is typescript trying to tell me something about the way I'm using a component as a prop? Is there a more proper way to do this? Thanks for reading.

1

There are 1 answers

1
Vaibhav Rai On

Check the code here, it's the issue of mismatching type ReactElement vs FunctionalComponent

Update:

Hey can you change it to

const ChapterTemplate = ({
    data,
    map,
}: { data: any, map: FunctionComponent<MapProps>}) => { // change this to ReactElement
  return (<div>Hello world</div>);
}

const Chapter: FunctionComponent<ChapterProps> = (props) => {
    return <ChapterTemplate data={props.data} map={Map} />;
};

and then do the <Map />

or do this

const ChapterTemplate = ({
    data,
    map,
}: { data: any, map: ReactElement}) => { // change this to ReactElement
  return (<div>Hello world</div>);
}

const Chapter: FunctionComponent<ChapterProps> = (props) => {
    return <ChapterTemplate data={props.data} map={<Map />} />;
};