Steps to developing a universal implementation of a generic interface in Typescript

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.

Answer №1

One possible solution is to create a factory function that generates instances of TableComponents:

function generateTableComponents<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} />)
    };
};

This approach would permit the following usage:

const CustomTableComponents_videos = generateTableComponents<Video>();
const CustomTableComponents_audio = generateTableComponents<Audio>();

Do you think this method could be effective?

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What are the steps to incorporate SignalR into a TypeScript project?

Working on an asp.net mvc 4.5 project with typescript in the client side, I successfully installed and configured signalR on the server side. To integrate it into my project, I also installed signalr.TypeScript.DefinitelyTyped with jquery. In my typescrip ...

Leveraging external Javascript files in Ionic framework version 3 and above

Hey there, I'm currently working on integrating the Mapwize SDK, an external Javascript library, with the latest version of Ionic. I've encountered the common challenge of getting Javascript to function smoothly with Typescript. Although I'm ...

The Jest mock is infiltrating the function logic instead of simply returning a rejected promise

The Issue at Hand I am currently working on testing the correct handling of errors when a use case function returns a rejected promise along with the appropriate status code. However, I seem to be encountering an issue where instead of getting a rejected ...

Can you provide input to the reducer function?

In the creator, an action is defined like this: export const actionCreators = { submitlink: (url: string) => <SubmitLinkAction>{ type: 'SUBMIT_LINK' } } In the component that calls it: public render() { return <div> ...

The Angular RXJS HTTPClient fails to properly handle HttpErrorResponses

In my Angular 12 project, I am facing an issue with setting up unit tests that should capture errors from HTTP responses. However, when running the tests, instead of receiving the error as an actual error object, it is being passed within the response body ...

Add a npm module without type definitions

I am currently utilizing Typescript version 2.1 and facing an issue with installing an npm package called 'reactable' that lacks typings. When attempting to import the package using import * as Reactable from 'reactable', Typescript di ...

Configuring Stylelint in a NextJS project using Emotionjs

I recently encountered an issue while trying to integrate Stylelint into a new NextJS Typescript project with EmotionJS. Many rules were not working in my styles files, and the only error I could identify was Unknown word CssSyntaxError. This particular U ...

Discover the latest DOM elements using Webdriverio 5

Currently, I am developing a REact based CMS application that features a form with multiple carousels. I am encountering an issue where the webdriverio implementation is unable to locate an element, even though the same xpath works fine when tested manua ...

The CORS policy specified in next.config.js does not appear to be taking effect for the API request

I am currently working on a Next.js application with the following structure: . ├── next.config.js └── src / └── app/ ├── page.tsx └── getYoutubeTranscript/ └── getYoutubeTranscript.tsx T ...

Whenever the route changes in Angular, the components are duplicated

Whenever I switch routes in my Angular application, such as going from home to settings and back to home, all the variables seem to be duplicated from the home page and are never destroyed. I noticed that I created a loop in the home component that displa ...

I encountered a typescript error while trying to export my Class component within a Higher Order Component (HOC)

I'm encountering a TypeScript error with my HomePage Class Component. Below is the code snippet: import * as React from "react"; import { Dispatch, AnyAction } from 'redux'; import { connect } from 'react-redux&apos ...

What is the relationship between Typescript references, builds, and Docker?

I am facing a dilemma with my projectA which utilizes a common package that is also needed by my other Nodejs services. I am unsure of the best approach to package this in a Docker file. Ideally, running tsc build would compile both the project and the dep ...

Using TypeScript to Return a Derived Class as a Method's Return Type

I'm currently facing a challenge with an abstract class in typescript that includes a method to provide a callback for later use. The issue lies in the return type of the method, as it is set to the class itself, preventing me from using fluent style ...

Comparing dates in Angular 6 can be done by using a simple

Just starting with angular 6, I have a task of comparing two date inputs and finding the greatest one. input 1 : 2018-12-29T00:00:00 input 2 : Mon Dec 31 2018 00:00:00 GMT+0530 (India Standard Time) The input 1 is retrieved from MSSQL database and the in ...

Combining certain key values from two dictionaries based on matching IDs in Angular

I am currently working with two arrays of JSON objects in Angular. Both dictionaries have a key-value pair called "song_id" in common. My goal is to combine the "rating" key-value from the second array into the first array where the song_id matches. Array ...

Issue with MIME handling while utilizing Vue-Router in combination with Express

Struggling to access a specific route in Express, I keep encountering an error in my browser. Additionally, when the Vue application is built, only the Home page and the 404 page seem to work properly, while the rest display a default empty HTML layout. F ...

Combining React Context and Typescript using a Custom Hook

I have been working on a context provider in React and Chakra-UI, but I seem to be facing some issues: import { useBoolean } from "@chakra-ui/react" import { createContext } from "react" const MobileContext = createContext<typeof us ...

"Learn how to automatically limit date selection to a maximum of 3 days after choosing a date with the nb-rangepicker rangep

I have successfully implemented the nebular date range picker to filter data. However, I am facing an issue with setting the default maxDate 3 days after selecting the input. I have tried multiple methods but none have worked for me. Below is my TypeScript ...

Ways to incorporate extension methods through a "barrel file" - how to do it?

When attempting to define extension methods in separate files and import them through a barrel file, the methods don't seem to be added to the prototype. The following approach works: import './rxjs-extensions/my-observable-extension-1'; i ...

NextJS and AWS Amplify collaboration for secure authentication routing

After hours of research, I'm struggling to navigate the authentication routing in NextJS combined with AWS Amplify. As a newcomer to NextJS, I want to implement a feature that disables the login/register page for users who are already logged in and pr ...