I am attempting to implement a table using generics in Typescript. The code I am using is based on a table component provided at:
https://www.bekk.christmas/post/2020/22/create-a-generic-table-with-react-and-typescript
I am uncertainn if the author of this code tested it before publishing it. Most of it appears to work, but there is a single compilation problem that I am hoping to get some insights on.
The test data provided is pretty straightforward:
interface Cat {
name: string;
age: number;
gender: string;
color: string;
activityLevel?: string; // optional, same as string | undefined
favoriteFood?: string; // optional, same as string | undefined
}
The main (Table) component, with props, is below. It is Table.tsx:
import React from "react";
import TableHeader from "./TableHeader";
import TableRows from "./TableRows";
export type ColumnDefinitionType<T, K extends keyof T> = {
key: K;
header: string;
width?: number;
}
export type TableProps<T, K extends keyof T> = {
data: Array<T>;
columns: Array<ColumnDefinitionType<T, K>>;
}
const style = {
borderCollapse: 'collapse'
} as const
const Table = <T, K extends keyof T>({ data, columns }: TableProps<T, K>): JSX.Element => {
return (
<table style={style}>
<TableHeader columns={columns} />
<TableRows
data={data}
columns={columns}
/>
</table>
);
};
export default Table;
The component design is pretty straightforward. The Table component has a header and body component.
The header component, TableHeader.tsx, is below:
import React from "react";
import {ColumnDefinitionType} from "./Table";
type TableHeaderProps<T, K extends keyof T> = {
columns: Array<ColumnDefinitionType<T, K>>;
}
const TableHeader = <T, K extends keyof T>({ columns }: TableHeaderProps<T, K>): JSX.Element => {
const headers = columns.map((column, index) => {
const style = {
width: column.width ?? 100, // 100 is our default value if width is not defined
borderBottom: '2px solid black'
};
return (
<th
key={`headCell-${index}`}
style={style}
>
{column.header}
</th>
);
});
return (
<thead>
<tr>{headers}</tr>
</thead>
);
};
export default TableHeader;
The body component, called TableRow.tsx, is below:
import React from "react";
import {ColumnDefinitionType} from "./Table";
type TableRowsProps<T, K extends keyof T> = {
data: Array<T>;
columns: Array<ColumnDefinitionType<T, K>>;
}
const style = {
border: '1px solid black'
}
const TableRows = <T, K extends keyof T>({ data, columns }: TableRowsProps<T, K>): JSX.Element => {
const rows = data.map((row, index) => {
return (
<tr key={`row-${index}`}>
{columns.map((column, index2) => {
return (
<td key={`cell-${index2}`} style={style}>
{row[column.key]}
</td>
);
}
)}
</tr>
);
});
return (
<tbody>
{rows}
</tbody>
);
};
export default TableRows;
For completeness, I am including the code for invoking the Table component:
import React from 'react';
import logo from './logo.svg';
import './App.css';
import Table from './Table';
interface Cat {
name: string;
age: number;
gender: string;
color: string;
activityLevel?: string; // optional, same as string | undefined
favoriteFood?: string; // optional, same as string | undefined
}
const data: Cat[] = [
{
name: "Mittens",
color: "black",
age: 2,
gender: "female",
activityLevel: "hight",
favoriteFood: "milk",
},
{
name: "Mons",
color: "grey",
age: 2,
gender: "male",
favoriteFood: "old socks",
activityLevel: "medium",
},
{
name: "Luna",
color: "black",
age: 2,
gender: "female",
activityLevel: "medium",
favoriteFood: "fish",
},
{
name: "Bella",
color: "grey",
age: 1,
gender: "female",
activityLevel: "high",
favoriteFood: "mice",
},
{
name: "Oliver",
color: "orange",
age: 1,
gender: "male",
activityLevel: "low",
favoriteFood: "fish",
},
];
const columns: ColumnDefinitionType<Cat, keyof Cat>[] = [
{
key: 'name',
header: 'Name',
width: 150
},
{
key: 'age',
header: 'Age in years',
},
{
key: 'color',
header: 'Color'
}
]
function App() {
return (
<div className="App">
<Table data={data} columns={columns} />
</div>
);
}
export default App;
When I attempt to compile this code, I am seeing one problem in the TableRow component. I get the following compilation error:
ERROR in src/TableRows.tsx:20:33
TS2322: Type 'T[K]' is not assignable to type 'ReactNode'.
Type 'T[keyof T]' is not assignable to type 'ReactNode'.
Type 'T[string] | T[number] | T[symbol]' is not assignable to type 'ReactNode'.
Type 'T[string]' is not assignable to type 'ReactNode'.
Type 'T[string]' is not assignable to type 'ReactPortal'.
Type 'T[keyof T]' is not assignable to type 'ReactPortal'.
Type 'T[K]' is not assignable to type 'ReactPortal'.
Type 'T[keyof T]' is not assignable to type 'ReactPortal'.
Type 'T[string] | T[number] | T[symbol]' is not assignable to type 'ReactPortal'.
Type 'T[string]' is not assignable to type 'ReactPortal'.
18 | return (
19 | <td key={`cell-${index2}`} style={style}>
> 20 | {row[column.key]}
| ^^^^^^^^^^^^^^^^^
21 | </td>
22 | );
23 | }
I have looked at documentation for React and Typescript, and I cannot see a reason for that line of code to fail to compile. Perhaps my understanding of string- based indexes is flawed?
Can someone give some idea of why this line of code is failing, and how I can get this TableRows component to work?
Top comments (0)