Home > Mobile >  Passing props in React jsx as generics
Passing props in React jsx as generics

Time:12-15

In my react app i want to pass a specific interface as a generic into a unspecific component.

For example i have three specific interfaces

SpecificInterfaces.jsx
export interface InterfaceA {
   name: string
   age: number
   ...
}

export interface InterfaceB {
    name: string
    movies: string[]
    count: number
    ...
}

export interface InterfaceC {
    name: string
    somestuff: someType
}

For each of the interfaces i have a specific component ComponentA, ComponentB and ComponentC. These Components need to be used in a shared component ComponentShared.

Now for example i want in my ComponentA to return SharedComponent with the generic Type of InterfaceA and props of Type InterfaceA like this:

ComponentA.jsx
export interface Props<T> {
    importData: T[]
    ... some props... 
}

const props: Props<InterfaceA> = {
importData: importData //This is from Interface Type InterfaceA
... someProps ...
}
  return (
        <React.Fragment>
            <SharedComponent<InterfaceA> {...props} />
        </React.Fragment>
    )

And in my sharedComponent i want to access the specific passed generic type like this:

SharedComponent.jsx
const SharedComponent= <T,>({
   importData,
   ...the passed Props
}: Props<T>): JSX.Element => {
importData.map((data: T) =>
    data.name) 

At importData.map((data:T) => data.name) it throws an error, saying T has no member of name. So i guess something isnt working with my generics i pass in here, because the InterfaceA im passing in as generic has the member "name" like any ohter InterfaceB and InterfaceC. What am i doing wrong?

CodePudding user response:

TypeScript doesn't know anything about the generic inside your function unless you inform it. You need to extend your generic T from a type that has the properties that you use inside the function. Consider this example:

TS Playground

function logNamesBroken <T>(objects: T[]): void {
  for (const obj of objects) {
    console.log(obj.name);
/*                  ^^^^
Property 'name' does not exist on type 'T'.(2339) */
  }
}

type BaseObject = {
  name: string;
};

function logNames <T extends BaseObject>(objects: T[]): void {
  for (const obj of objects) {
    console.log(obj.name); // ok now
  }
}

More, based on the code in your question:

TS Playground

import {default as React} from 'react';

interface BaseItem {
  name: string;
}

interface InterfaceA extends BaseItem {
  age: number;
}

interface Props<T extends BaseItem> {
  importData: T[];
}

const SharedComponent = <T extends BaseItem>({
  importData,
}: Props<T>): React.ReactElement => {
  return (
    <ul>
      {
        importData.map((data, index) => (
          <li key={`${index}-${data.name}`}>{data.name}</li>
        ))
      }
    </ul>
  );
};

const importData: InterfaceA[] = [{name: 'a', age: 1}, {name: 'b', age: 2}];

const props: Props<InterfaceA> = {
  importData,
};

const AnotherComponent = (): React.ReactElement => (
  <React.Fragment>
    <SharedComponent {...props} />
  </React.Fragment>
);
  • Related