Тип возврата TypeScript React HOC, совместимый с декоратором

Я использую React Native с TypeScript. Я написал HOC, который мне нравится использовать в качестве декоратора, чтобы дать компонентам значок:

import React, { Component, ComponentClass, ReactNode } from "react";
import { Badge, BadgeProps } from "../Badge";

function withBadge<P>(
  value: number,
  hidden: boolean = value === 0
): (WrappedComponent: ComponentClass<P>) => ReactNode {
  return (WrappedComponent: ComponentClass<P>) =>
    class BadgedComponent extends Component<P> {
      render() {
        return (
          <React.Fragment>
            <WrappedComponent {...this.props} />
            {!hidden && <Badge value = {value} />}
          </React.Fragment>
        );
      }
    };
}

export default withBadge;

Проблема в том, что когда я пытаюсь использовать этот компонент в качестве декоратора, вот так:

import React, { PureComponent } from "react";
import { Icon } from "react-native-elements";
import { getIconName } from "../../services/core";
import withBadge from "../WithBadge/withBadge";
import styles from "./styles";

@withBadge(1)
export default class BadgedCart extends PureComponent {
  render() {
    return (
      <Icon
        type = "ionicon"
        name = {getIconName("cart")}
        containerStyle = {styles.iconRight}
        onPress = {() => {
          // Nothing.
        }}
      />
    );
  }
}

Я получаю сообщение об ошибке:

[ts]
Unable to resolve signature of class decorator when called as an expression.
  Type 'null' is not assignable to type 'void | typeof BadgedCart'. [1238]

Я пробовал разные другие типы возвращаемых данных, такие как JSX.Element или ReactElement<any>, но работает только any, который сводит на нет цель TypeScript. Какой тип возвращаемого значения должны иметь компоненты более высокого порядка?

Редактировать: Когда я меняю тип возвращаемого значения (например, Правин) на typeof PureComponent, ошибка для @withBadge(1) меняется на:

[ts]
Unable to resolve signature of class decorator when called as an expression.
  Type 'typeof PureComponent' is not assignable to type 'typeof BadgedOrders'.
    Type 'PureComponent<any, any, any>' is not assignable to type 'BadgedOrders'.
      Types of property 'render' are incompatible.
        Type '() => ReactNode' is not assignable to type '() => Element'.
          Type 'ReactNode' is not assignable to type 'Element'.
            Type 'undefined' is not assignable to type 'Element'. [1238]

Если попробую поменять просто на PureComponent

    class BadgedComponent extends Component<P> {
      render() {
        return (
          <React.Fragment>
            <WrappedComponent {...this.props} />
            {!hidden && <Badge value = {value} />}
          </React.Fragment>
        );
      }
    };

Выдает ошибку:

[ts]
Type '(WrappedComponent: ComponentClass<P, any>) => typeof BadgedComponent' is not assignable to type '(WrappedComponent: ComponentClass<P, any>) => PureComponent<{}, {}, any>'.
  Type 'typeof BadgedComponent' is not assignable to type 'PureComponent<{}, {}, any>'.
    Property 'context' is missing in type 'typeof BadgedComponent'. [2322]
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
3
0
2 060
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Первая проблема заключается в том, что P не в том положении, в котором о нем можно было бы предположить. Поскольку он находится на withBadge, и этот вызов не будет содержать его.

Вторая проблема заключается в том, что декоратор класса должен возвращать void или тот же тип, что и входной класс:

declare type ClassDecorator = <TFunction extends Function>(target: TFunction) => TFunction | void;

Это означает, что подпись декоратора не может быть (WrappedComponent: ComponentClass<P>) => ReactNode, так как она возвращает ReactNode. Это даже не может быть (WrappedComponent: ComponentClass<P>) => ComponentClass<P>, поскольку он не возвращает то же самое, что и переданный. Решение состоит в том, чтобы немного солгать компилятору и объявить, что мы возвращаем void, когда фактически возвращаем новый класс. Это не будет иметь большого значения во время выполнения:

class Badge extends React.Component<{ value: number }> { 

}

function withBadge(
value: number,
hidden: boolean = value === 0
): <P extends object>(WrappedComponent: ComponentClass<P>) => void {
return <P extends object>(WrappedComponent: ComponentClass<P>) =>
    class BadgedComponent extends Component<P> {
    render() {
        return (
        <React.Fragment>
            <WrappedComponent {...this.props} />
            {!hidden && <Badge value = {value} />}
        </React.Fragment>
        );
    }
    };
}

@withBadge(1)
export default class BadgedCart extends PureComponent {
    render() {
        return (
        <div />
        );
    }
}

Вы можете отказаться от декоратора в пользу простого вызова функции:

class Badge extends React.Component<{ value: number }> { 

}

function withBadge(
    value: number,
    hidden: boolean = value === 0
): <P extends object>(WrappedComponent: ComponentClass<P>) => ComponentClass<P> {
return <P extends object>(WrappedComponent: ComponentClass<P>) =>
    class BadgedComponent extends Component<P> {
        render() {
            return (
            <React.Fragment>
                <WrappedComponent {...this.props} />
                {!hidden && <Badge value = {value} />}
            </React.Fragment>
            );
        }
    };
}

const  BadgedCart = withBadge(1)(class extends PureComponent {
    render() {
        return (
        <div />
        );
    }
});
export default BadgedCart;

Вы можете добавить функцию в класс BadgedCart, покажет ошибку.

maquannene 27.11.2019 15:17

Другие вопросы по теме