TypeScript `typeof React.Component` не расширяет интерфейс? Проблема с определением компонента более высокого порядка

Я создаю компонент более высокого порядка, чтобы обернуть компонент, расширяющий интерфейс:

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

Созданная мной фабрика ожидает React.ComponentClass реализации 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>)
      }
   }
}

Чтобы протестировать фабрику, я реализовал компонент, расширяющий 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>
    )
  }
}

Но когда я использую компонент в качестве аргумента в функции clickOutside:

const withClickOutside = clickOutside(Test)

Я получаю следующую ошибку типа для аргумента Test:

Аргумент типа typeof Test не может быть назначен параметру типа ComponentClass & ClickOutsideInterface. Тип typeof Test нельзя присвоить типу ClickOutsideInterface. Свойство onClickOutside отсутствует в типе typeof Test.

Есть идеи, почему Typescript считает, что я не реализую интерфейс в Test?


person tgk    schedule 29.10.2017    source источник
comment
Дело не в декораторе? withClickOutside()(Test)   -  person Dmitriy Kovalenko    schedule 29.10.2017
comment
возможно - да :)   -  person Dmitriy Kovalenko    schedule 29.10.2017
comment
Декоратор @DmitriyKovalenko или нет, если бы эта функция была декоратором, предложенный вами вызов был бы неверным, потому что это вызов фабрики декораторов, а не вызов декоратора. Это было бы действительным только в том случае, если clickOutside вернул функцию, которую можно было бы использовать в качестве декоратора. Это в любом случае не имело бы здесь смысла, потому что для этого не потребовалось бы никаких аргументов.   -  person Aluan Haddad    schedule 29.10.2017


Ответы (1)


TypeScript говорит, что вы не реализовали интерфейс, требуемый вашей функцией для своего аргумента, потому что на самом деле вы этого не сделали.

Когда ты пишешь

class A {
  //...
}

Вы определяете два типа и одно значение.

значение с именем A - это класс. Это функция, которую вы вызываете с new для создания объектов.

Первый тип, который также называется A, является типом объектов, которые создаются путем вызова функции класса с new, как описано выше.

Второй тип - это тип функции этого класса. Объявление класса не создает автоматически имя для этого типа, но тип существует, тем не менее, потому что все значения имеют тип. Этот тип записывается typeof A, потому что это тип значения A, тип класса.

Следовательно, в зависимости от того, чего вы пытаетесь достичь, вам необходимо либо

Передайте экземпляр Test

const withClickOutside = clickOutside(new Test);

Или измените свою функцию так, чтобы она принимала объект, соответствующий типу самого класса, а не объекты, которые он создает.

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

Я подозреваю, что в данном случае вам нужно последнее.

Наконец, хотя вам может понадобиться это для целей документации, что является полностью допустимым, я хотел бы отметить, что нет никаких оснований фактически объявлять, что ваш класс вообще реализует интерфейс. TypeScript - это язык со структурной типизацией, и все интерфейсы реализуются с помощью совместимых членов.

person Aluan Haddad    schedule 29.10.2017
comment
спасибо Алуан! Мне удалось заставить его работать, используя, как вы рекомендовали, `Component: new (props: P) =› React.Component ‹P› & ClickOutsideInterface` Спасибо! Я хотел бы иметь возможность ссылаться на React.ComponentClass displayName или методы, представленные в ClickOustideInterface, например Component.onClickOutside в компоненте HO - есть ли способ получить доступ к типу возвращаемого значения функции Component? что-то вроде Component: new(props: P) => (C: React.Component<P> & ClickOutsideInterface) - person tgk; 29.10.2017
comment
@tgk - это статические члены класса? - person Aluan Haddad; 29.10.2017
comment
вы можете заставить его принимать любые аргументы-ограничители, которые вам нравятся, используя синтаксис, который вы упомянули - person Aluan Haddad; 29.10.2017
comment
нет, к сожалению, они не являются статическими членами класса - мне как-то нужно реструктурировать мою фабрику, чтобы иметь доступ к экземпляру. - person tgk; 30.10.2017