Pesquisa de site

reactjs - Typescript `typeof React.Component` não estende a interface? Problema ao definir componente de ordem superior


Estou criando um componente de ordem superior para envolver um componente que estende a interface:

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

A fábrica que eu criei espera um React.ComponentClass que implemente a 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>)
      }
   }
}

Para testar a fábrica, implementei um Componente que estende a 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>
    )
  }
}

Mas quando eu uso o componente como um argumento na função clickOutside:

const withClickOutside = clickOutside(Test)

Eu recebo o seguinte erro de tipo para o argumento Test:

O argumento do tipo 'typeof Test' não é atribuível ao parâmetro do tipo 'ComponentClass & ClickOutsideInterface'. O tipo 'typeof Test' não é atribuível ao tipo 'ClickOutsideInterface'. A propriedade 'onClickOutside' está ausente no tipo 'typeof Test'.

Alguma ideia de por que o Typescript acredita que eu não estou implementando a interface em Test?

Resposta #1

O TypeScript diz que você não implementou a interface que sua função exige de seu argumento porque você de fato não implementou.

Quando você escreve

class A {
  //...
}

Você define dois tipos e um valor.

O valor, que é chamado A, é a classe. É a função que você chama com new para criar objetos.

O primeiro tipo, que também é chamado de A, é o tipo dos objetos que são criados ao chamar a função de classe com new conforme descrito acima.

O segundo tipo é o tipo daquela função de classe. Declarar uma classe não cria automaticamente um nome para esse tipo, mas o tipo existe mesmo assim porque todos os valores têm um tipo. Esse tipo é escrito typeof A porque é o tipo do valor A, o tipo da classe.

Portanto, dependendo do que você está tentando realizar, você precisa ou

Passe uma instância de Test

const withClickOutside = clickOutside(new Test);

Ou altere sua função para que ela receba um objeto correspondente ao tipo da própria classe, e não aos objetos que ela cria

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

Suspeito que seja o último que você quer neste caso.

Por último, embora você possa querer isso para fins de documentação, o que é completamente válido, gostaria de notar que não há razão para realmente declarar que sua classe implementa a interface. TypeScript é uma linguagem com tipagem estrutural e todas as interfaces são implementadas ao ter membros compatíveis.