6

I want to create a generic Table component.


type HeadCell<DataType> = {
  id: keyof DataType;
  label: string;
};

type TableProps<DataType> = {
  heads: HeadCell<DataType>[];
  rows: Array<DataType>;
};

const Table = ({ heads, rows }: TableProps) => {
  const ColumnsKeys = heads.map(
    (item: { [key: string]: any }) => item.id
  );

  return (
    <table>
      <tr>
        {heads.map((head: string, headKey: number) => {
          return (
            <th key={headKey}>{head.label}</th>
          );
        })}
      </tr>

      {rows.map((row, rowKey) => {
        return (
          <tr key={rowKey}>
            {ColumnsKeys.map((column: string, columnKey: number) => {
              return (
                <td key={columnKey}>{row[column]}</td>
              );
            })}
          </tr>
        );
      })};  

    </table>
  );
};

This way, the idea is that I can easily create Table like:

Example 1:

const heads = [
  {
    id: 'firstname',
    label: 'Firstname'
  },
  {
    id: 'lastname',
    label: 'Lastname'
  }
];

const rows = [
  {
    firstname: 'John',
    lastname: 'Adams'
  },
  {
    firstname: 'Paul',
    lastname: 'Walker'
  },
];

<Table heads={heads} rows={rows} />

Example 2:

const heads = [
  {
    id: 'company',
    label: 'Company'
  },
  {
    id: 'nb_employees',
    label: 'Number of employees'
  },
  {
    id: 'country',
    label: 'Country'
  }
];

const rows = [
  {
    company: 'Vody aho',
    nb_employees: 1590,
    country: 'Hong Kong'
  },
  {
    company: 'Royal spirit',
    nb_employees: 15,
    country: 'USA'
  },
];

<Table heads={heads} rows={rows} />

Now from a typescript point of view, I have a problem to pass the DataType which is a parameter of the type of the props TableProps

How could i handle this? Can I pass type Typescript to Props react? or is there a way to do this dynamically?

Knowing that for these 2 examples therefore :

Exemple1:

type DataType = {
  firstname: string;
  lastname: string;
}

Exemple2:

type DataType = {
  company: string;
  nb_employees: number;
  country: string;
}

How can I manage TableProps<DataType> type in react component props. Knowing that it will be a generic Table component => so DataType is practically dynamic.

Thanks

Hasina Njaratin
  • 401
  • 1
  • 6
  • 17

3 Answers3

7

Use generics to infer the type from the data you pass. You'll need to convert the component from an arrow function to a standard function, because TS can't do generics with JSX in an arrow function.

Example: (sandbox).

type HeadCell<DataType> = {
  id: Extract<keyof DataType, string>;
  label: string;
};

type TableProps<DataType> = {
  heads: HeadCell<DataType>[];
  rows: Array<DataType>;
};

export function Table<T>({ heads, rows }: TableProps<T>) {
  const ColumnsKeys = heads.map((item: HeadCell<T>) => item.id);

  return (
    <table>
      <tr>
        {heads.map((head, headKey) => {
          return <th key={headKey}>{head.label}</th>;
        })}
      </tr>
      {rows.map((row, rowKey) => {
        return (
          <tr key={rowKey}>
            {ColumnsKeys.map((column: keyof T, columnKey) => {
              return <td key={columnKey}>{row[column]}</td>;
            })}
          </tr>
        );
      })}
    </table>
  );
}
Ori Drori
  • 183,571
  • 29
  • 224
  • 209
6

This can be done with the arrow function according to typescript documentation https://wanago.io/2020/02/17/typescript-generics-discussing-naming-conventions/ Arrow functions section

 export const Table = <T,>({ heads, rows }: TableProps<T>) => {

    
libik
  • 61
  • 1
  • 2
  • Did you notice that your answer is almost identical to the already accepted answer? Is there any additional information about your answer that you can share to make it not seem like you copied it? – treckstar Jul 01 '22 at 05:08
  • 7
    My answer is saying that you still can use the arrow function component in this case. Edited to include only changed code if that makes it a bit more clear, thanks – libik Jul 01 '22 at 06:39
0

Both the above answers are correct. In case someone is wondering how to pass the custom type while invoking <Table />, here's how you can do it -


type DataType = {
  firstname: string;
  lastname: string;
}

 <Table<DataType> heads={heads} rows={rows} />


sohammondal
  • 635
  • 6
  • 12