Using a Typescript generic type as an argument for a class constructor

Currently, I am working on incorporating the repository generic pattern with Facebook dataloader for a GraphQL API.

In an abstract base class where the error occurs, here is the code snippet:

import { Collection, Db, InsertOneWriteOpResult, ObjectId } from "mongodb";
import { RepositoryGeneric } from "../../types/database";

export default abstract class BaseRepository<TEntity, TLoaders> implements RepositoryGeneric<TEntity> {
    protected mongoCollection: Collection<TEntity>;
    protected dataLoaders: TLoaders;

    public constructor(database: Db, collection: string, loaders: TLoaders) {
        this.mongoCollection = database.collection(collection);
        this.dataLoaders = loaders;
    }

    public async findOne(id: ObjectId | string): Promise<TEntity | null> {
        if (typeof id === "string") {
            id = ObjectId.createFromHexString(id);
        }

        // The issue arises at this line
        return this.dataLoaders.id.load(id);
    }

    public async create(entity: TEntity): Promise<TEntity> {
        const result: InsertOneWriteOpResult = await this.mongoCollection.insertOne(entity);

        if (!result.result.ok) {
            throw new Error();
        }

        return result.ops[0];
    }
}

The next section showcases a class that extends the aforementioned abstract base class:

import { MongoClient } from "mongodb";
import { EntityBusiness } from "../../types/database";
import BusinessLoaders from "../loaders/businessLoaders";
import BaseRepository from "./baseRepository";

export default class BusinessRepository extends BaseRepository<EntityBusiness, BusinessLoaders> {
    public constructor(mongoClient: MongoClient, businessLoaders: BusinessLoaders) {
        super(mongoClient.db(), "business", businessLoaders);

        // Ensure no errors are present
        console.log(businessLoaders.id);
    }
}

Lastly, there's a class focused on implementing the dataloader functionality:

import DataLoader from "dataloader";
import { AggregationCursor, Collection, MongoClient, ObjectId } from "mongodb";
import { EntityBusiness, LoaderCommon } from "../../types/database";

export default class BusinessLoaders implements LoaderCommon<EntityBusiness> {
    private idLoader: DataLoader<ObjectId, EntityBusiness>;

    public constructor(mongoClient: MongoClient) {
        this.idLoader = new DataLoader((keys) => this.idBatch(mongoClient, keys), {
            cacheKeyFn: (key) => key.toHexString(),
        });
    }

    public get id(): DataLoader<ObjectId, EntityBusiness> {
        return this.idLoader;
    }

    private async idBatch(mongoClient: MongoClient, keys: ObjectId[]): Promise<EntityBusiness[]> {
        const collection: Collection<EntityBusiness> = mongoClient.db().collection("business");
        const aggregation: AggregationCursor<EntityBusiness> = collection.aggregate([
            { $match: { _id: { $in: keys } } },
            { $addFields: { __order: { $indexOfArray: [keys, "$_id"] } } },
            { $sort: { __order: 1 } },
            { $project: { __order: 0 } },
        ]);

        return aggregation.toArray();

    }
}

Even though I lack extensive experience in typescript, I didn't anticipate any errors but encountered the following:

Error TS2339: Property 'id' does not exist on type 'TLoaders'.

Answer №1

To ensure that your generic type includes certain constraints, such as having an id property, you can define this using the extends keyword within the generic type declaration:

interface ILoadersInterface<TEntity> {
  id: DataLoader<ObjectId, TEntity | null>
}

export default abstract class BaseRepository<TEntity, TLoaders extends ILoadersInterface<TEntity>> 
  implements RepositoryGeneric<TEntity> {
    // ...
}

In general, it's important for a class to contain enough type information internally to accurately resolve all code usage. In cases where TypeScript cannot infer from usage that TLoaders will have an id property, you need to explicitly set constraints like extends Interface, extends string, etc.

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

Insert the template's content into my inline component

I am facing an issue with a template I have created. The template structure is as follows: <template> <my-body-component inline-component> <slot/> </my-body-component> </template> My goal is to make my-body-component ...

Access the array values by their respective keys in an object that is returned from a custom JavaScript file utilizing the Node.js file system

I recently came across a config file with a unique format, as shown below: define([], function () { return { productItems: { item1: ['Apple', 'Ball', 'Car'], item2: [&apo ...

I am encountering compilation errors while trying to run a basic react-dnd list using typescript

I'm currently working on implementing the beautiful-react-dnd example and encountering some errors: import * as React from 'react'; import { DragDropContext, Draggable, Droppable, DroppableProvided, DraggableLocation, DropResult, ...

Why is it that TypeScript does not issue any complaints concerning specific variables which are undefined?

I have the following example: class Relative { constructor(public fullName : string) { } greet() { return "Hello, my name is " + fullName; } } let relative : Relative = new Relative("John"); console.log(relative.greet()); Under certain circum ...

I am currently attempting to implement a redirect feature after logging in using Angular. However, I encountered an error stating, "The argument of type 'string | null' cannot be assigned to a parameter of type 'string | UrlTree'."

Below is the code snippet from my auth.service.ts file: import { Injectable } from "@angular/core"; import { GoogleAuthProvider } from 'firebase/auth'; import { AngularFireAuth } from '@angular/fire/compat/auth'; import { Obse ...

Enhancing Angular input validators with updates

Working on a project with Angular 6, I have set up an input field using mat-input from the Angular Material framework and assigned it an id for FormGroup validation. However, when I initialize my TypeScript class and update the input value, the validator d ...

Unable to create Azure CDN Endpoint: HostName entered is invalid - Ensure it is a valid domain name, IP version 4, or IP version 6

As I work on setting up an Azure CDN endpoint using CDKTF, I've hit a roadblock with an error that I'm unable to troubleshoot. The error message I'm seeing states: Error: creating Endpoint: (Name "test" / Profile Name "test-profile" / Resour ...

Error encountered after deploying Angular 4 application with Webpack 3 in a production environment

Upon successfully building with angular4+webpack3, I encountered an error on the second refresh in the browser. The error message was: uncaught exception: reflect-metadata shim is required when using class decorators To resolve this issue, I had to add ...

Dealing with precompile array warning when utilizing a basic router in Angular 2

I am currently working on developing a straightforward router-based application in Angular2 using typescript. The version of Angular2 I am using is 2.0.0-rc.4, and the router version is 3.0.0-beta.1 Here is my Routes configuration- App.routes.ts import ...

What could be causing the "undefined object" warning within this optional chain?

I encountered this issue: buyTicketData?.pricingOptions resulting in this error message: [tsl] ERROR in /Applications/MAMP/htdocs/wp-content/plugins/tikex/tikexModule/components/BuyTicket/PricingOptionInvoiceItemsFormFieldsCheckboxes.tsx(280,25) TS2 ...

Verification Tool for Detecting Repeated Spaces

I am working towards developing an Angular Validator that will prevent consecutive spaces from being entered. At the moment, I have implemented Validators.pattern('^[a-zA-Z0-9]+( [a-zA-Z0-9]+)*$'), which successfully addressed the issue. However ...

After performing the `ng build --prod` command in Angular 4, deep linking functionality may not

I have a requirement to display different screens in my Angular application based on the URL pasted by the user in the browser: http://localhost/screen1 (should show screen1) http://localhost/screen2 (should show screen2) To achieve this, I have set up ...

Experiencing a hitch when attempting to deploy an Angular 2 application on Heroku

Encountering the sh: 1: tsc: not found Error when attempting to deploy an Angular 2 app on Heroku. Using Node version: v7.2.0 and npm Version: v4.0.3. View the error on Heroku Any suggestions on how to resolve this issue? ...

Issue with demonstrating the Material Angular expansion-panel feature

Exploring the world of Material Angular has been an exciting journey. Recently, I've been experimenting with adding components to my project. Currently, I'm focused on incorporating the expansion-panel component example into a dialog. However, de ...

Exploring the concepts of type intersection in TypeScript

I attempted to create a type definition for recurrent intersection, in order to achieve this specific behavior: type merged = Merged<[{a: string}, {b: string}, ...]> to end up with {a: string} & {b: string} & ... I came up with some type u ...

Who is responsible for the addition of this wrapper to my code?

Issue with Sourcemaps in Angular 2 TypeScript App Currently, I am working on an Angular 2 app using TypeScript, and deploying it with the help of SystemJS and Gulp. The problem arises when I try to incorporate sourcemaps. When I use inline sourcemaps, eve ...

Creating a Personalized NPM Package: Revealing Specialized Functions for Your Unique NPM Package

Currently, I have a few npm packages that are crucial for our internal company projects. I am looking to open up some of the interfaces utilized by these dependencies from within this component. For instance, in a project involving an npm package named "c ...

What is the process for modifying object information using string input in typescript?

How can I update an object's value in TypeScript based on a string input related to the object key? const objData = { // random value A: 11, B: 13, C: 53, innerObj: { AStatus: true, BStatus: false, CStatus: true, }, }; type Item ...

Transitioning menus in Ionic 2

I followed the instructions in the Ionic 2 menu documentation and tried to display the menu in a specific way: https://i.sstatic.net/zzm8f.png My intention was to have the menu displayed below the content page while keeping the menu button visible. Howe ...

Making an HTTP request with headers in Angular 2

I am currently in the process of developing an application that interacts with the Twitter API. I am facing a challenge when attempting to make an HTTP GET request with specific headers using Angular2. It is worth noting that the curl requests below are s ...