Creating a Typescript version of the mongodb project aggregation functionality

Present scenario: I am currently working on creating a type-safe wrapper for the node-mongodb driver. I am facing challenges in determining the return type for the project aggregation stage.

Feel free to check out the TypeScript Playground here

class BaseAggregate<Coll> {
  private pipeline: Pipeline<Coll>[] = [];
  constructor(initialPipeline?: typeof this.pipeline) {
    if (initialPipeline) {
      this.pipeline = initialPipeline;
    }
  }

  match(stageInput: DeepPartial<Coll>) {
    return new BaseAggregate<Coll>([...this.pipeline, { $match: stageInput }]);
  }

  projectActual(stageInput: Record<keyof Coll, 0 | 1>) {
    return new BaseAggregate<T>>([
      //  what goes here ----^ instead of T?
      //   ...this.pipeline,
      //   { $project: stageInput },
    ]);
  }
}


interface Test {
  name: string;
  email: string;
}

const agg = new BaseAggregate<Test>()
  .match({ name: "john" })
  .match({ email: "sir john" })
  .match({ email: "sir" })
  .projectActual({ email: 0, name: 1 })

Seeking assistance in defining the return type of the projectActual() function in order to achieve the desired return type as shown in the example above.

interface {
name: string;
}

Your time and help are greatly appreciated 🙏

Answer №1

It appears that for projectActual(), the desired generic call signature should be as follows:

declare projectActual<R extends Record<keyof T, 0 | 1>>(
  stageInput: R
): BaseAggregate<{ [K in keyof T as R[K] extends 1 ? K : never]: T[K]; }>

In this case, the stageInput should be of a generic type R that is constrained to Record<keyof T, 0 | 1>. This means that the keys in stageInput must match those in T, with values limited to either 0 or 1.

For the output type of BaseAggregate, the type argument is

{ [K in keyof T as R[K] extends 1 ? K : never]: T[K] }
. This represents a key-remapped mapped type where each key K is retained if the corresponding value in R is 1, otherwise it is omitted (never means exclusion). Essentially, this operation is akin to a Pick<T, Keys> where Keys comprises keys in R</code with values of <code>1.


Let's conduct a test:

 interface Test {
  name: string;
  email: string;
}

const agg = new BaseAggregate<Test>()
  .match({ name: "john" })
  .match({ email: "sir john" })
  .match({ email: "sir" })
  .projectActual({ email: 0, name: 1 })

/* const agg: BaseAggregate<{
    name: string;
}> */

The outcome seems positive. Upon inspection, agg has the type

BaseAggregate<{name: string}>
; the suppression of the email property has been successful.

Link to Playground for Code

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

Tips for managing Vue component content prior to data being fully loaded

I'm currently integrating product category data from Prismic into my Nuxt project and seeking guidance on best practices. Specifically, I need clarity on managing the state when data is still being fetched and there's no content to display in the ...

Creating multiple-to-multiple relationships in Express: A beginner's guide

In developing a small API with Express and TypeScript, I am faced with handling both POST and GET requests. The POST request involves receiving a list of organizations, which may have daughter organizations that can also have their own daughters, creating ...

The TypeScript error message indicates that the property 'forEach' is not found on the 'FileList' type

Users are able to upload multiple files on my platform. After uploading, I need to go through each of these files and execute certain actions. I recently attempted to enhance the functionality of FileList, but TypeScript was not recognizing the forEach m ...

Can the data cells of columns be dynamically adjusted to align them on a single vertical line?

For some time now, I have been grappling with a CSS issue. I am working with a table that has 3 columns displaying departures, times, and situational text for scenarios like delays or cancellations. However, as evident from the images, the alignment of th ...

Utilize the automatically detected type of an object for utilization in a Generic context in Typescript

The Scenario I am experimenting with the combination of Alpine.js and TypeScript. To achieve this, I am utilizing the community-maintained typings package @types/alpinejs (GitHub) along with the reusable components design pattern outlined here. Here' ...

Can you clarify the significance of the "1" in this particular Typescript code snippet?

Can anyone provide some insight into the purpose of this '1' in the following TS code snippet? decryptPassPhrase() { this.$encrypt.setPrivateKey(privateKey); this.decryptedPassPhrase = this.$encrypt.decrypt(this.encryptedPassPhrase); ...

ReactJS - Opt for useRef over useState for props substitution

Presented below is my ImageFallback component, which serves as a backup by displaying an svg image if the original one is not available. export interface ImageProps { srcImage: string; classNames?: string; fallbackImage?: FallbackImages; } const Im ...

Ways to retrieve the property of an object within a view while being sourced from an observable

I am currently working with the following provider code: getWorldCities2() { return this.http.get('../assets/city.list.json') .map(res => res.json()); } Within my TypeScript code, I have implemented the following: ionViewDidLoad() { ...

Is Axios the sole option for API calls when utilizing Next.js with SSG and SSR?

Can someone clarify the best practice for data fetching in Next.js? Should we avoid using axios or other methods in our functional components, and instead rely on SSG/SSR functions? I'm new to Next.js and seeking guidance. ...

Tips for implementing JS function in Angular for a Collapsible Sidebar in your component.ts file

I am attempting to collapse a pre-existing Sidebar within an Angular project. The Sidebar is currently set up in the app.component.html file, but I want to transform it into its own component. My goal is to incorporate the following JS function into the s ...

What is the function of the OmitThisParameter in TypeScript when referencing ES5 definitions?

I came across this specific type in the ES5 definitions for TypeScript and was intrigued by its purpose as the description provided seemed quite vague. /** * Removes the 'this' parameter from a function type. */ type OmitThisParameter<T> ...

The sequence of initializing test hooks in inconsistent playwright tests

My testing framework setup looks something like this: test.describe("...", () => { let p: Page; test.beforeEach(async({browser}) => { p = await (await browser.newContext()).newPage(); } test(...); test(...); test.aft ...

Can I create a unique Generic for every Mapped Type in Typescript?

I've got a function that accepts multiple reducers and applies them all to a data structure. For instance, it can normalize the data of two individuals person1 and person2 using this function: normalizeData([person1, person2], { byId: { init ...

Struggling to implement JSS hover functionality in a project using React, Typescript, and Material UI

I am a newcomer to the world of React/Typescript/Material UI and I am trying to understand how to work with these technologies together. While researching, I came across a similar question related to using hover with Material-UI. However, the syntax was d ...

Encountering a 400 bad request error while trying to retrieve an authentication token from an API url in Angular

I encountered a problem in my Angular 6 application where I am receiving an Http 400 Bad Request error when attempting to call the API url for login token. The interesting thing is that the API works perfectly fine when accessed through POSTMAN. However, ...

Using System.import in my code is triggering a cascade of errors in my console

I incorporate the System module in my component. export class LoginComponent { constructor() { System.import('app/login/login.js'); } } The file loads successfully, however, TypeScript compiler raises an error Error:(10, 9) TS2 ...

Why are the class variables in my Angular service not being stored properly in the injected class?

When I console.log ("My ID is:") in the constructor, it prints out the correct ID generated by the server. However, in getServerNotificationToken() function, this.userID is returned as 'undefined' to the server and also prints as such. I am puzz ...

Mocking store.dispatch in Jest with TypeScript did not result in any function calls being made

Testing Troubles I'm a beginner in the world of testing and I'm facing some challenges. Despite going through all the documentation on jest, I couldn't find information specific to TypeScript cases. Currently, I'm on a quest to figure ...

The defineProps<SomePropType>() method is not rendering the props as expected

I have various components, with a parent element where I attempted to pass props using the syntax defineProps<{}>(). The setup is simple, I have a types.ts file at the root level, a parent Vue file (referred to as CardItem), and multiple components. ...

Metronome in TypeScript

I am currently working on developing a metronome using Typescript within the Angular 2 framework. Many thanks to @Nitzan-Tomer for assisting me with the foundational concepts, as discussed in this Stack Overflow post: Typescript Loop with Delay. My curren ...