What is the best approach to obtain a Generic and static reference to a MongoDB collection?

My goal is to create a generic and static class method called getOne<T>() that can return MongoDB objects as an Item, where the Item can be either a Book or a Film. Initially, I had an idea on how to achieve this, but now I am struggling with dynamically using the collection names 'books' or 'films' instead of 'xxx'.


    import * as mongodb from 'mongodb';

    const CONNECTION_STRING = 'MY_CONNECTION_STRING';
    const BOOK_COLLECTION_NAME = 'books';
    const FILM_COLLECTION_NAME = 'films';

    interface Item {
        _id: string
    }

    interface Book extends Item {
        bookName: string,
        description?: string
    }

    interface Film extends Item {
        filmTitle: string,
        duration?: number
    }

    function isBook(item: Item): item is Book {
        return 'bookName' in item;
    }

    async function getOne<T extends Item>(): Promise<T | null> {

        const client = new mongodb.MongoClient(CONNECTION_STRING);
        await client.connect();
        const db = client.db();

        const collection = db.collection<T>('xxx');
        // Instead of xxx I need to use BOOK_COLLECTION_NAME or FILM_COLLECTION_NAME
        // depending on the actual type of T, how do I do that???
        const item = await collection.findOne({});
        if (item !== null) {
            console.log(item._id);
            if (isBook(item)) {
            // Book type specific implementation
            item.bookName += ' (best book ever)';
            }
        }
        return item as T;
    }

    async function test() {
        const book: Book | null = await getOne<Book>();
        console.log(book?.bookName);
        const film: Film | null = await getOne<Film>();
        console.log(film?.filmTitle);
    }

    test();
    

My question is, is it possible to achieve this without passing the collection name as an argument in the function call (e.g. getOne<Book>('books'))? I want to avoid that and maintain the desired usage shown in the test() function.

Answer №1

The exact API you desire does not support accessing type parameters passed into a function at runtime, making it impossible to infer the table name. A workaround is to have the name of the interface inferred from the collection name. For example, passing 'books' would return Book | null, passing 'films' would return Film | null, and passing any other value would result in a compile time error.

type Collections = {
  books: Book,
  films: Film,
}

async function getOne<Name extends keyof Collections>(name: Name): Promise<Collections[Name] | null> {
  // fetch the actual value here
  return {} as Collections[Name];
}
  
async function test() {
  const book: Book | null = await getOne('books');
  console.log(book?.bookName);
  const film: Film | null = await getOne('films');
  console.log(film?.filmTitle);
  // Argument of type '"comments"' is not assignable to parameter of type 'keyof Collections'.
  await getOne('comments');
}

test();

Playground

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

Guide to setting a SetState function within a component (Using NextJS and TypeScript)

I'm currently diving into TypeScript, and I've hit a roadblock when it comes to the correct assignment for my setState function that I need to pass to a component. /index.tsx import { Dispatch, SetStateAction, useState } from "react"; ...

Exploring the process of retrieving data from localStorage in Next.js 13

Having recently delved into the realm of Next JS, I've encountered a hurdle when it comes to creating middleware within Next. My aim is to retrieve data from local storage, but I keep hitting roadblocks. middleware.ts import { key, timeEncryptKey, to ...

Tips for efficiently deconstructing JSON arrays, objects, and nested arrays

I'm attempting to destructure a JSON file with the following structure: [ { "Bags": [ { "id": 1, "name": "Michael Kors Bag", "price": 235, "imgURL" ...

Troubleshooting issue with alignment in Material UI using Typescript

<Grid item xs={12} align="center"> is causing an issue for me No overload matches this call. Overload 1 of 2, '(props: { component: ElementType<any>; } & Partial<Record<Breakpoint, boolean | GridSize>> & { ...

Transferring Parameters to EJS Template in Node.js

Hey guys, I'm having an issue with rendering a MongoDB record in my .ejs file. Strangely, when I console.log the variable before the end of my route, I get the expected value. However, it becomes undefined in the .ejs file. Here is the code snippet: ...

Ways to pass functions as properties to a React custom modal?

I'm currently working on a React login page setup. The login functionality is embedded in a Modal using the React-Modal library. When a user presses the login button, data is supposed to be sent to a Custom Modal as props to display a notification win ...

Angular: Enable function to await Observable completion before returning result

I require assistance with the user function below: getUser(uuid: string): Observable<WowUserDataModel> { let user: WowUserDataModel = { login: null, userUuid: uuid, firstName: null, lastName: null, displayName: nul ...

Send regex to MongoDB using HTTP protocol

Successfully executed a basic regular expression in the mongo console: db.users_exams.count({"id_number": {$regex: /1234/}}) Now, I face the challenge of passing this regex to mongo after going through 2 micro services: python micro service -> http ...

Is there a way to iterate through documents in Mongoose using NodeJS?

Two collections are being used here - Let's call them the main and sub collections. The main collection is structured as follows: { "_id" : "xxxxx", "subId" : "1234" } The sub collection has a different structure: { "_id" : "1234", "name" : ...

Utilizing NPM Workspaces to efficiently distribute TypeScript definition files (`*.d.ts`) across multiple workspaces

In my TypeScript monorepo utilizing NPM Workspaces, I have two packages: A and B. Package B requires type definitions from package A. To accomplish this, I included a reference to A's definition file in the tsconfig.json of package B. However, somet ...

Update each number in an array by appending a string within a table in an Angular component, rather than the

I have created a function that decides on a comment based on the result number added to an array and displays it in a table. However, calling this function within the template is causing a performance slowdown. Is there a way to achieve the same outcome w ...

What is the best way to incorporate auto-completion into a browser-based editor using Monaco?

Recently, I embarked on a project to develop a browser-based editor using monaco and antlr for a unique programming language. Following an excellent guide, I found at , gave me a great start. While Monaco provides basic suggestions with ctrl + space, I am ...

The module 'module://graphql/language/parser.js' could not be resolved

I'm facing an issue while creating a React Native TypeScript project on Snack Expo. Even though I have added graphql to the package.json and included the types file, I keep getting this error : Device: (1:8434) Unable to resolve module 'module:/ ...

Guide to accessing a newly opened window from a different domain originating from the current window

Currently working on an Angular project, I am facing a scenario where I have a link on page A that directs users to a different origin page B. The HTML code for the link is shown below: ... <a href="https://another.origin"> PAGE B </a> ... On ...

Sorting TypeScript types by required properties first

Can anyone recommend a plugin or ESLint rule that can automatically sort types by assuming required fields come first, followed by optional fields? Here's an example before: type TExampleSorting = { title?: string; value?: number; text: string; ...

Require assistance in accurately assigning a date to a Date[] in Typescript Array without altering current elements

In my current code, I have a loop that verifies if a date is a holiday and then adds it to an array. The issue I'm facing is that whenever I assign a new element to the array, all previous data in the list gets updated to the latest element. Here&apos ...

Attempting to send numerous identifiers in an API request

I encountered a problem while working on a function in Angular that involves pulling data from an API. My goal is to enhance a current segment to accommodate multiple IDs, but I face difficulties when attempting to retrieve more than one ID for the API que ...

Detecting changes in a readonly input in Angular 4

Here is a code snippet where I have a readonly input field. I am attempting to change the value of this readonly input from a TypeScript file, however, I am encountering difficulty in detecting any changes from any function. See the example below: <inp ...

Ways to bypass HostListener in Angular 2

In the process of developing a page with animations triggered on scroll, I encountered the challenge of ensuring that the animations only occur when the corresponding element is visible on the viewport. Utilizing Angular2 for this task led me to investigat ...

Decorators in Angular 9 do not have the capability to support function expressions

Currently, I am working with Angular 9 and facing an issue while generating dynamic routes values during runtime. I have implemented a ComplexUrlRouter to achieve this functionality and integrated it into my Route configuration. However, I encountered the ...