I am looking to implement the DRY principle in a specific scenario. I have a generic interface called "SomeInterface<Data = unknown>", and I need multiple implementations of this interface that are essentially identical except for the type definition. I want to create a generic class/interface/expression that captures these commonalities.
To provide more clarity, I am working with react-virtuoso
and I need different TableComponents
similar to what follows. Instead of defining separate components like
VirtuosoTableComponents_booklists
and VirtuosoTableComponents_books
, I aim to have something like VirtuosoTableComponents<T>
which can be used as components={VirtuosoTableComponents<booklists>}
.
tables.tsx
import { Book, BookList } from './data-context';
const VirtuosoTableComponents_booklists: TableComponents<BookList> = {
// eslint-disable-next-line react/display-name
Scroller: React.forwardRef<HTMLDivElement>((props, ref) => <TableContainer component={Paper} {...props} ref={ref} />),
Table: props => <Table {...props} sx={{ borderCollapse: 'separate', tableLayout: 'fixed' }} />,
TableHead,
TableRow: props => <TableRow {...props} />,
// eslint-disable-next-line react/display-name
TableBody: React.forwardRef<HTMLTableSectionElement>((props, ref) => <TableBody {...props} ref={ref} />)
};
const VirtuosoTableComponents_books: TableComponents<Book> = {
// eslint-disable-next-line react/display-name
Scroller: React.forwardRef<HTMLDivElement>((props, ref) => <TableContainer component={Paper} {...props} ref={ref} />),
Table: props => <Table {...props} sx={{ borderCollapse: 'separate', tableLayout: 'fixed' }} />,
TableHead,
TableRow: props => <TableRow {...props} />,
// eslint-disable-next-line react/display-name
TableBody: React.forwardRef<HTMLTableSectionElement>((props, ref) => <TableBody {...props} ref={ref} />)
};
export const MyTables = (): React.JSX.Element => {
const [data, updateData] = React.useState(undefined as unknown as CtxData);
React.useEffect(() => {
// .... fetch data ....
updateData(response);
}
// function fixedHeaderContent_booklists ..
// function fixedHeaderContent_books ..
// function rowContent_booklists ..
// function rowContent_books ..
return (
<>
<DataContext.Provider value={data}>
<div><p>Book Lists</p></div>
<TableVirtuoso
data={data.booklists.rows}
components={VirtuosoTableComponents_booklists}
fixedHeaderContent={fixedHeaderContent_booklists}
itemContent={rowContent_booklists}
/>
<div><p>Books</p></div>
<TableVirtuoso
data={data.books.rows}
components={VirtuosoTableComponents_books}
fixedHeaderContent={fixedHeaderContent_books}
itemContent={rowContent_books}
/>
</DataContext.Provider>
</>
)
}
data-context.ts
export interface BookList {
bookRef: string[]
}
export interface Book {
title: string;
author: string;
}
export interface ColumnData<T> {
dataKey: keyof T;
label: string;
width: number;
}
export interface CtxData {
booklists: { rows: BookList[]; columns: ColumnData<BookList>[] };
books: { rows: Book[]; columns: ColumnData<Book>[] };
}
I have attempted to extract a new interface without success.