简体   繁体   中英

How to correctly Type a React Component that is a param of a Function?

I have a function, which takes two params: a react component, and a string. I am trying to give them correct typings, however, am confused as to why the following wont work...

The problem is with the typing with GivenComponent in the Function. By giving the param that specific typing of Component<>, the return of the GivenComponent (within the < Query />) errors stating the following:

JSX element type 'GivenComponent' does not have any construct or call signatures. [2604]

However, how would you type that correctly? That param is a React Component, could be both a functional or a class component. As seen in the second part of code, the param isn't given as < MyComponent />, it is given as MyComponent - so not the "rendered" jsx? maybe that changes things

Function:

Basically takes a component and returns it with a higher order component wrapper around it

import React, { Component } from "react";

const withQueryData: Function = (
  GivenComponent: Component<{ data: {} }, any>,
  query: string
) => () => (

  <Query query={gql(query)}>
    {({ loading, error, data }) => {
      if (loading) return <p>Loading...</p>;
      if (error) return <p>Error! {error.message}</p>;

      return <GivenComponent data={data} />;
    }}
  </Query>
);

export default withQueryData;

React Component:

example of how the function above is called with params

class MyComponent extends Component<...removed...> {

  render() {
    return (
      <div>...</div>
    );
  }
}



const MyComponentQuery = `query goes here...`;

const MyComponentWithData = withQueryData(
  MyComponent,
  MyComponentQuery
);

export default MyComponentWithData;

Transforming my comment into an answer.

There are two typings for declared react components:

React.ComponentClass - a typing for react component that is declared as class:

class SomeComponent extends React.Component {...} // this is React.ComponentClass

React.FunctionComponent is a typing for function component (CO to the rescue! :D)

const MyFunctionComponent: React.FunctionComponent = props => "something"

So, if your component is one of ComponentClass or FunctionComponent , you can use Union Type and tell the TypeScript about it in this way:

const SomeUnionType: React.ComponentClass | React.FunctionComponent

Thats it! Hope, that helps :)

You need to use ComponentType to represent a component type (functional or class, defined in react definitions as type ComponentType<P = {}> = ComponentClass<P> | FunctionComponent<P>; ).

Also you might want to allow the HOC to forward properties from the wrapped component.

Also remove the Function annotation from withQueryData as that will remove all type safety from withQueryData .

import { Component, ComponentType } from "react";
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
const withQueryData = <P extends { data: {} }>(
  GivenComponent: ComponentType<P>,
  query: string
) => (p: Omit<P, 'data'>) => (

  <Query query={gql(query)}>
    {({ loading, error, data }) => {
      if (loading) return <p>Loading...</p>;
      if (error) return <p>Error! {error.message}</p>;

      return <GivenComponent data={data} {...p as any} />;
    }}
  </Query>
);

class MyComponent extends Component<{ data: {}, otherProp: string }> {

  render() {
    return (
      <div>...</div>
    );
  }
}



const MyComponentQuery = `query goes here...`;

const MyComponentWithData = withQueryData(
  MyComponent,
  MyComponentQuery
);

export default MyComponentWithData;

let o = () => <MyComponentWithData otherProp="" ></MyComponentWithData>  // otherProp required, data provided bu the HOC

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM