web-dev-qa-db-fra.com

Type de composant générique sans état React? OR Étendre l'interface de fonction générique dans TypeScript pour avoir un autre générique?

Problème: L'interface de Stateless Functional Component est donnée comme 

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

Le type de prop de mon composant est également générique:

interface Prop<V>{
    num: V;
}

Comment définir correctement mon composant? comme:

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

donne une erreur à character 27 que Cannot find name 'T'

Voici: TypeScript Playground de l'exemple modifié

MyFindings

1: TypeScript 2.9.1 prend en charge le composant générique avec état: 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: Étendre SFC pour créer une nouvelle interface, comme indiqué dans la réponse suivante, définirait le type prop du composant comme suit: any: TypeScript Fonction sans état avec les paramètres/types de retour génériques que je ne souhaite pas. Je veux donner le bon type pour mon accessoire

5
Naman Kheterpal

Vous ne pouvez pas utiliser de génériques comme ceci:

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

La spécification TypeScript déclare:

Une construction de la forme

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

pourrait être analysé comme une expression de fonction de flèche avec un paramètre de type ou une assertion de type appliquée à une fonction de flèche sans paramètre de type.

la source; Microsoft/TypeScript spec.md

Votre déclaration ne correspond pas au modèle défini dans la spécification TypeScript, elle ne fonctionnera donc pas.

Vous pouvez cependant ne pas utiliser l'interface SFC et simplement la déclarer vous-même.

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>
    );
}
5
jmattheis

Modèle d'usine:

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>();
2
chris

Il existe un modèle pour atténuer ce problème en déclarant un alias de type de composant générique en dehors de composant, puis en l'affirmant simplement lorsque vous en avez besoin.

Pas aussi joli, mais toujours réutilisable et strict.

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>
0
vadistic

Tu as ceci:

interface Prop<V> {
    num: V;
}

Et vous avez défini votre composant comme ceci:

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

Cela ne fonctionnerait pas car vous auriez besoin de fournir un type concret pour la variable V dans l'interface puisque vous l'implémentez dans votre composant. 

Cela ressemble à quelque chose comme ça:

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

Notez mon utilisation de object où vous aviez T. C'est juste un exemple.

0
John Kennedy