Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How to create a generic implementation of a generic interface in typescript?

I would like to apply DRY to the following situation. There is a generic interface "SomeInterface<Data = unknown>", and I need several implementations of that interface, which happen to be all equal except for the explicit type definition. I would like to have a generic class/interface/expression that collects all the commonalities.

For more clarity, I am using react-virtuoso and I want several TableComponents like the following. Instead of declaring VirtuosoTableComponents_booklists and VirtuosoTableComponents_books, I would like to have something like VirtuosoTableComponents<T> and then use it like components={VirtuosoTableComponents<booklists>}.

tables.tsx

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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 tried extracting a new interface, but no success.

>Solution :

You could create a factory-like function that creates the TableComponents objects:

function createTableComponents<T>(): TableComponents<T> {
    return {
        // 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} />)
    };
};

Which would allow you to do:

const VirtuosoTableComponents_booklists = createTableComponents<BookList>();
const VirtuosoTableComponents_books = createTableComponents<Book>();

Would that work?

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading