Reputation: 481
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
Upvotes: 8
Views: 12668
Reputation: 191976
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>
);
}
Upvotes: 9
Reputation: 739
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} />
Upvotes: 1
Reputation: 61
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>) => {
Upvotes: 6