9

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

3 Answers 3

9

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>
  );
}
Sign up to request clarification or add additional context in comments.

3 Comments

cool! thx! so where/how can I set in the DataType type? through <T>?
You don't need to set it. TS populates T by inferring the types from the array.
yes, it works.thx
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>) => {

    

2 Comments

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?
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
1

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} />


2 Comments

i feel somehow iffy about this lol
why? what do you mean? 🤔

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.