15

I'm working with React 16.2.0, TypeScript 2.7.1, and any is not allowed as a type.

The main component:

// index.js

import * as React from 'react'
import Page from './page'
import i18n from '../i18n'

import PageContent from '../components/pageContent'
import withMoreInfo from '../hoc/withMoreInfo'

class Home extends React.Component {
  render () {
    return <Page title={ i18n.t('home.title') }>
      <PageContent />
    </Page>
  }
}

export default withMoreInfo(Home)

The hoc file:

import * as React from 'react'

export default function withMoreInfo<T> (Wrapped: T) {
  return class WithMoreInfo extends React.Component<{ asPath: string }> {
    static async getInitialProps ({ asPath }: { asPath: string }) {
      return { asPath }
    }

    render () {
      const { asPath } = this.props
      const language = asPath.indexOf('/ro') === 0 ? 'ro' : 'en'
      return <Wrapped language={ language } pathname={ asPath } />
    }
  }
}

I can't solve this error: error #TS2604: JSX element type 'Wrapped' does not have any construct or call signatures.

enter image description here

Any hint is very much appreciated. Thanks, Paul

2 Answers 2

8

You need to tell the compiler that the parameter is a constructor function and that returns a React component with the properties language and pathname

function withMoreInfo<T extends React.Component<{ language: string, pathname: string }, any>>(Wrapped: new (props: { language: string, pathname: string }, context?: any) => T) {
    return class WithMoreInfo extends React.Component<{ asPath: string }> {
        static async getInitialProps({ asPath }: { asPath: string }) {
            return { asPath }
        }

        render() {
            const { asPath } = this.props
            const language = asPath.indexOf('/ro') === 0 ? 'ro' : 'en'
            return <Wrapped language={language} pathname={asPath} />
        }
    }
}
// The component must have properties language and pathname and only those
class Home extends React.Component<{ language: string, pathname: string }> {
    render() {
        return <div />
    }
}

export default withMoreInfo(Home)

In your original version when you invoke withMoreInfo(Home), T would have indeed been a react component, however you could have just as well invoked withMoreInfo(1) since T was in no way constrained. The generic function must be correct for any type that is passed to it, so the compiler considered T as being possibly anything and so it could reliably say nothing about it. The solution is to let the compiler know, that the Wrapped parameter is a constructor of a react component, namely any react component T that has as properties { language: string, pathname: string }. A constructor function has a similar signature declaration to a regular function, just with the new keyword, hence new (props: { language: string, pathname: string }, context?: any) => T

Sign up to request clarification or add additional context in comments.

1 Comment

Titian, thank you so much for your reply! I've been searching for that answer for a while and haven't seen any article or piece of code with this setup. This is precious. Question 1: I was expecting "T" to be of type Home (which extends React.Component). It's counter-intuitive to have to redeclare the extension. Can you explain why or give me a hint/link where to read about this? Question 2: you use the "new" instruction without a name. Does TS infer it from <T extends React.Component<MoreInfo>>? That's not very clear for me. Thanks again for your precious help. Paul
1

Putting an answer here because it is relevant to the error in general.

I was missing new inside the type definition.

some-js-component.d.ts file:

import * as React from "react";

export default class SomeJSXComponent extends React.Component<any, any> {
    new (props: any, context?: any)
}

and inside the tsx file where I was trying to import the untyped component:

import SomeJSXComponent from 'some-js-component'

...inside render()
   return (
        <React.Fragment>
            <SomeJSXComponent withProps={asdf} />
        </React.Fragment>
   );

Comments

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.