Typescript перегружается универсальными компонентами, выводом типов и компонентами реакции

#reactjs #typescript #mixins #higher-order-components #react-tsx

Вопрос:

Приношу извинения за дурацкое название вопроса. Вот подробности.

У меня есть функция HoC/Mixin

 export function MyMixin<TOriginalProps>(WrappedComponent: React.ComponentClass<TOriginalProps amp; IExtraProps>): React.ComponentClass<TOriginalProps> {
  return class extends React.Component<TOriginalProps> {
    public render() {
      const value = "something irrelevent to the question"
      const props = {...this.props, extraProp: value}
      return <WrappedComponent {...props} />;
    }
  }
}

export interface IExtraProps {
  extraProp: string;
}
 

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

 class MyComponent extends React.Component<MyProps amp; IExtraProps, MyState>

MyMixin(MyComponent) // this returns React.ComponentClass<TMyProps,any>
 

однако для компонентов, у которых нет никаких реквизитов (кроме реквизита для «смешивания»), это дает мне что-то немного странное

 class MyComponentNoProps extends React.Component<IExtraProps, MyState>

MyMixin(MyComponentNoProps) // this return React.ComponentClass<{children?: React.Node; extraProp: string;}, any>

 

Я попытался адаптировать функцию MixinFunction, чтобы она принимала значения по умолчанию для параметра типа, но я просто не могу заставить ее принимать.

Есть идеи, как я могу использовать одну функцию (перегрузки тоже были бы хороши) для этого?

Я пробовал перегрузку, у которой нет типа TOriginalProps, но она не устраняется должным образом. Если я определю это первым, то все будут использовать его, а мои компоненты, у которых должны быть реквизиты, — нет. Если я определю его вторым, никто им не воспользуется.

Я предполагаю, что это связано с тем фактом, что я использую React как нечто, выводящее этот странный тип {children: React.Node; extraProps: string} откуда-то (

Ответ №1:

Перегруженная версия, которая IExtraProps специально нацелена, по-видимому, создает поведение, которое вы ищете:

 interface MyProps {
  originalProp: number;
}

interface IExtraProps {
  extraProp: string;
}

interface MyState {
  value: string;
}

export function MyMixin(
  WrappedComponent: React.ComponentClass<IExtraProps>
): React.ComponentClass;

export function MyMixin<TOriginalProps>(
  WrappedComponent: React.ComponentClass<TOriginalProps amp; IExtraProps>
): React.ComponentClass<TOriginalProps>;

export function MyMixin(
  WrappedComponent: React.ComponentClass<any>
): React.ComponentClass {
  return class extends React.Component {
    public render() {
      const value = "something irrelevent to the question"
      const props = {...this.props, extraProp: value}
      return <WrappedComponent {...props} />;
    }
  }
}

class MyComponent extends React.Component<MyProps amp; IExtraProps, MyState>{};
class MyComponentNoProps extends React.Component<IExtraProps, MyState>{};

const Test1 = MyMixin(MyComponent);
const Test2 = MyMixin(MyComponentNoProps);

function App() {
  return (
    <>
      <Test1 originalProp={42} /> // OK 
      <Test2 /> // OK
    </>
  )
}
 

Комментарии:

1. Спасибо за предложение, однако, когда я пытаюсь это сделать, я не получаю тех же результатов. По существу, и компонент, и ComponentNoProps вызывают, в которых сначала определяется любая перегрузка, так как и то, и другое может быть выполнено. Оба реализуют IExtraProps, поэтому может применяться не универсальный «фиксированный» метод IExtraProps, и поскольку TOriginalProps может быть просто {}, кажется, что вторая перегрузка тоже соответствует обоим

2. @Dave Я не могу воспроизвести вашу игровую площадку . Не могли бы вы попробовать создать минимальный тестовый случай, в котором это не удастся?