Problems compiling a generic component
RobertBrown3
Posted on February 3, 2024
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?
Posted on February 3, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.