2

I'm creating a higher-order component to wrap a component extending the interface:

interface ClickOutsideInterface {
  onClickOutside: (e: Event) => void
}

The factory I've created expects a React.ComponentClass implementing the ClickOutsideInterface:

  function clickOutside<P>(Component: React.ComponentClass<P> & ClickOutsideInterface){    
    return class ClickOutside extends React.Component<P, {}> { 
      /* on click outside invoke Component.onClickOutside  */

      render() {
        return(<div><Component {...this.props} /></div>)
      }
   }
}

To test the factory, I've implement a Component extending the ClickOutsideInterface

class Test extends React.Component<TProps, TState> implements ClickOutsideInterface {
  onClickOutside(event: Event): void {
    this.setState({
      closed: true
    })
  }

  render(){
    return(
      <div>{this.state.closed}</div>
    )
  }
}

But when I use the component as an argument in the function clickOutside:

const withClickOutside = clickOutside(Test)

I get the following type error for argument Test:

Argument of type 'typeof Test' is not assignable to parameter of type 'ComponentClass & ClickOutsideInterface'. Type 'typeof Test' is not assignable to type 'ClickOutsideInterface'. Property 'onClickOutside' is missing in type 'typeof Test'.

Any ideas why Typescript believes I'm not implementing the interface in Test?

3
  • Its not about decorator? withClickOutside()(Test) Commented Oct 29, 2017 at 16:33
  • probably yes :) Commented Oct 29, 2017 at 16:33
  • @DmitriyKovalenko decorator or not, if this function were a decorator your suggested invocation would be incorrect because it is a decorator factory invocation not a decorator invocation. It would only be valid if clickOutside returned a function which could be used as a decorator. That wouldn't make sense here anyway because it wouldn't take any arguments. Commented Oct 29, 2017 at 16:52

1 Answer 1

4

TypeScript says that you haven't implemented the interface your function requires of its argument because you indeed have not.

When you write

class A {
  //...
}

You define two types and one value.

The value, which is named A, is the class. It is the function that you call with new to create objects.

The first type, which is also named A, is the type of the objects that are created by calling the class function with new as described above.

The second type is the type of that class function. Declaring a class doesn't automatically create a name for this type but the type exists none the less because all values have a type. This type is written typeof A because it is the type of the value A, the type of the class.

Therefore, depending on what you're trying to accomplish, you need to either

Pass an instance of Test

const withClickOutside = clickOutside(new Test);

Or change your function so that it takes an object matching the type of the class itself, and not the objects it creates

function clickOutside<P>(
  Component: new () => React.ComponentClass<P> & ClickOutsideInterface
) {...}

I suspect it is the latter that you want in this case.

Lastly, while you may want it for documentation purposes which is a completely valid, I'd like to note that there's no reason to actually declare that your class even implements the interface at all. TypeScript is a structurally typed language and all interfaces are implemented by having compatible members.

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

4 Comments

thanks Aluan! I was able to get it working by using, as you recommended, ` Component: new(props: P) => React.Component<P> & ClickOutsideInterface` Thanks! I'd like to be able to reference React.ComponentClass displayName or the methods exposed on ClickOustideInterface like Component.onClickOutside within the HO Component -- is there a way to access the return type of the function Component ? somethign like Component: new(props: P) => (C: React.Component<P> & ClickOutsideInterface)
@tgk are those static members of the class?
you can make it take any constrictor arguments that you like using the syntax that you mentioned
no unfortunately they are not static members of the class -- I somehow need to restructure my factory to have access to the instance.

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.