Тип общего безголового компонента? ИЛИ Расширение универсального интерфейса функции в машинописном тексте, чтобы иметь более общий характер?

Проблема: интерфейс Stateless Functional Component задан как

interface SFC<P = {}> {
    (props: P & { children?: ReactNode }, context?: any): ReactElement<any> | null;
    propTypes?: ValidationMap<P>;
}

Тип реквизита моего компонента также является общим:

interface Prop<V>{
    num: V;
}

Как правильно определить мой компонент? как:

const myCom: <T>SFC<Prop<T>> = <T>(props: Prop<T>)=> <div>test</div>

выдает ошибку в character 27 которая Cannot find name 'T'

Вот: { (props: Readonly

context?: any): number; displayName?: string; defaultProps?: Partial

; } interface Prop{ num: V; } const Search: FunctionalComponent >= (props) => { return 10; } rel=noreferrer>Typescript Детская площадка модифицированного примера

MyFindings:

1: Typescript 2.9.1 поддерживает общий компонент Stateful: http://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-9.html#generic-type-arguments-in-jsx-elements

class myCom<T> extends React.Component<Prop<T>, any> {
   render() {
      return <div>test</div>;
   }
}

2: Расширение SFC для создания нового интерфейса, как упомянуто в следующем ответе, сделало бы тип prop компонента как any: Typescript Реагирует на функцию без сохранения состояния с общими типами параметров/возвращаемых значений, которые мне не нужны. Я хочу дать правильный тип для моей опоры

Ответ 1

Вы не можете использовать дженерики, как это:

const myCom: <T>SFC<Prop<T>> = <T>(props: Prop<T>)=> <div>test</div>

Спецификация TypeScript гласит:

Конструкция формы

< T > ( ... ) => { ... }

может быть проанализирован как выражение функции стрелки с параметром типа или утверждением типа, примененным к функции стрелки без параметра типа.

источник; Microsoft/TypeScript spec.md

Ваше объявление не соответствует шаблону, определенному в спецификации TypeScript, поэтому оно не будет работать.

Однако вы не можете использовать интерфейс SFC и просто объявить его самостоятельно.

interface Prop<V> {
    num: V;
}

// normal function
function Abc<T extends string | number>(props: Prop<T>): React.ReactElement<Prop<T>> {
    return <div />;
}

// const lambda function
const Abc: <T extends string | number>(p: Prop<T>) => React.ReactElement<Prop<T>> = (props) => {
   return <div />
};

export default function App() {
    return (
        <React.Fragment>
            <Abc<number> num={1} />
            <Abc<string> num="abc" />
            <Abc<string> num={1} /> // string expected but was number
        </React.Fragment>
    );
}

Ответ 2

Существует шаблон, позволяющий смягчить эту проблему, объявляя псевдоним универсального типа компонента вне компонента, а затем просто утверждая его, когда это необходимо.

Не так красиво, но все же многократно и строго.

interface IMyComponentProps<T> {
  name: string
  type: T
}

// instead of inline with component assignment
type MyComponentI<T = any> = React.FC<IMyComponentProps<T>>

const MyComponent: MyComponentI = props => <p {...props}>Hello</p>

const TypedComponent = MyComponent as MyComponentI<number>

Ответ 3

Заводская модель:

import React, { SFC } from 'react';

export interface GridProps<T = unknown> {
  data: T[];
  renderItem: (props: { item: T }) => React.ReactChild;
}

export const GridFactory = <T extends any>(): SFC<GridProps<T>> => () => {
  return (
    <div>
      ...
    </div>
  );
};

const Grid = GridFactory<string>();

Ответ 4

У вас есть следующее:

interface Prop<V> {
    num: V;
}

И у вас есть свой компонент, определенный следующим образом:

const myCom: SFC<Prop<T>> = <T>(props: Prop<T>)=> <div>test</div>

Это не сработает, так как вам нужно будет указать конкретный тип для V в интерфейсе, так как вы реализуете его в своем компоненте.

Это выглядит примерно так:

const myCom: SFC<Prop<object>> = <T>(props: Prop<T>)=> <div>test</div>

Обратите внимание на мое использование object где у вас есть T Это просто пример.

Ответ 5

Вы можете отказаться от аннотирования с помощью React.FC и просто написать:

const myCom = <T>(props: Prop<T>) => <div>test</div>

Ответ 6

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

import React, { memo } from 'react';

export type FormDefaultProps<T> = {
  initialValues: T;
  onSubmit<T>(values: T, actions: FormikActions<T>): void;
  validationSchema?: object;
};

// We extract React.PropsWithChildren from React.FunctionComponent or React.FC
function component<T>(props: React.PropsWithChildren<FormDefaultProps<T>>) {
  // Do whatever you want with the props.
  return(<div>{props.children}</div>
}

// the casting here is key. You can use as typeof component to 
// create the typing automatically with the generic included..
export const FormDefault = memo(component) as typeof component;

И затем, вы используете это как:

 <FormDefault<PlanningCreateValues>
        onSubmit={handleSubmit}
        initialValues={PlanningCreateDefaultValues}
      >
         {/*Or any other child content in here */}
        {pages[page]}
</FormDefault>

Я не смог достичь этого с помощью выражений методов:

const a: React.FC<MyProp> = (prop) => (<>MyComponent</>);