Search code examples
reactjstypescriptreact-propstypescript-genericsreact-typescript

Typescript in Reactjs How to make dynamic Props type?


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


Solution

  • 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>
      );
    }