What is the best way to propagate a generic type throughout the entire application?

Is there a way to pass generic types deep into my application? I am developing a middleware that needs to be compatible with any framework. I am initializing the middleware with generic values, expecting them to be passed down to internal classes and methods. However, it seems like this is not happening:

app.use(
  umbress<express.Request, express.Response>({
    ipAddressExtractor: (request) => request.headers['x-forwarded-for'],
    // -----------------^^^^^^^^^------------------------
    // The request type here should be express.Request, but it's showing as R
  }),
);

The umbress function is initialized as follows:

export default function umbress<R, S>(userOptions: UmbressOptions): (request: R, response: S) => void {
  const processor = new ProcessorService<R, S>(userOptions, optionsServiceInstance, ipBasedMitigationServiceInstance);
  //------------------------------------^^^^^^----------------------------------
  // Passing down generic values here

  return processor.process;
}

In addition, the ProcessorService also accepts generics:

export class ProcessorService<R, S> {

The method process uses these generics for argument types:

async process(request: R, response: S): Promise<S | void> {
    const ipAddress = this.#options.ipAddressExtractor<R>(request);

The ipAddressExtractor is defined inside the type object like this:

ipAddressExtractor<R>(request: R): string;

Have I overlooked something? How can I resolve this issue?

EDIT: Here's a complete example that you can run:

type UmbressOptions = {
  ipAddressExtractor<R>(request: R): string;
};

class ProcessorService<R, S> {
  #options: UmbressOptions;

  constructor(userOptions: UmbressOptions) {
    this.#options = userOptions;
  }

  async process(request: R): Promise<S | void> {
    const ipAddress = this.#options.ipAddressExtractor<R>(request);
  }
}

function umbress<R, S>(userOptions: UmbressOptions): (request: R, response: S) => void {
  const processor = new ProcessorService<R, S>(userOptions);
  // ------------------------------------^^^^^^----------------------------------
  // passing down generic values here

  return processor.process;
}

umbress<Request, Response>({
  ipAddressExtractor: (request) => request.headers['x-forwarded-for'],
  // -----------------^^^^^^^^^-----------^^^^^^^^--------------------
  // `request` implicitly has type `any` because R is not set to a default value
});

You might need to install npm i defaults cache-manager for full type resolution.

Answer №1

To enhance the functionality of your UmbressOptions, it's advisable to make it generic as shown below:

type UmbressOptions<Request> = CachingOptions & {
  ipAddressExtractor(request: Request): string;
};

Similarly, your OptionsService should also be made generic:

class OptionsService<Request> {
  mergeDefaultsAndUserProvidedOptions<T extends Record<string, unknown> = UmbressOptions<Request>>(
    defaults: T,
    userOptions: T,
  ): T {
    return combine<T, T>(defaults, userOptions);
  }
}

Upon updating the umbress function by passing the R type parameter into UmbressOptions, you can achieve improved functionality:

function umbress<R, S>(userOptions: UmbressOptions<R>): (request: R, response: S) => void {
  const processor = new ProcessorService<R, S>(userOptions, optionsServiceInstance);
  
  return processor.process;
}

Now, when invoking the umbress function, the request is inferred correctly as follows:

app.use(
  umbress<express.Request, express.Response>({
    ipAddressExtractor: (request) => request['x-forwarded-for'],
  }),
);

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

Creating a legitimate Angular 6 form模shape

I want to reset my form using the following method: public static resetDescriptionFields(i: any, component: any) { var formItems = component.form.get('items') as FormArray; var descriptionItem = formItems.controls[i].g ...

How to Alphabetize an Array of Strings Containing Numbers using TypeScript and Angular

I'm working with an array that looks like this: arr = ["100 abc", "ad", "5 star", "orange"]; The goal is to first sort the strings without numbers at the beginning, then sort the strings with numbers added at t ...

Using `await` inside an if block does not change the type of this expression

Within my code, I have an array containing different user names. My goal is to loop through each name, verify if the user exists in the database, and then create the user if necessary. However, my linter keeps flagging a message stating 'await' h ...

How can we create dynamic keys for object properties in typescript?

Is there a way in TypeScript to convert an array of objects into an object with keys and arrays dynamically? For instance, given the following data: data1 = [ {a: 'st1', b: 1, c: 1, d: 1, e: 'e1' }, {a: 'st2', b: 2, c: 2, ...

Customize global history type in Typescript

In my TypeScript package, I have a global type file that contains the definition for the history object as shown below: lib.dom.d.ts interface History { readonly length: number; scrollRestoration: ScrollRestoration; readonly state: any; ba ...

Implementing HTTP GET and POST requests in Angular 2 allows for the functionality of displaying, adding, and deleting objects within a list

Hey there, I'm completely new to dealing with HTTP and fetching data from the server. I've been scouring through various tutorials, examples, and questions on different platforms, but unfortunately, I haven't been able to find exactly what I ...

I am struggling to comprehend the concept of dependency injection. Is there anyone available to provide a clear explanation for me?

I am working on a NestJS application and trying to integrate a task scheduler. One of the tasks involves updating data in the database using a UserService as shown below: import { Injectable, Inject, UnprocessableEntityException, HttpStatus, } fro ...

Injecting dynamic templates in Angular 7

Let me simplify my issue: I am currently using NgxDatatable to display a CRUD table. I have a base component named CrudComponent, which manages all CRUD operations. This component was designed to be extended for all basic entities. The challenge I am en ...

Changing Enum Value to Text

In my enum file, I have defined an object for PaymentTypes: export enum PaymentTypes { Invoice = 1, CreditCard = 2, PrePayment = 3, } When I fetch data as an array from the database, it also includes PaymentType represented as numbers: order: ...

Ignore TypeScript errors when using TSNode with Mocha by forcing the compiler to emit code despite errors and having TSNode execute the emitted code

Below is a code snippet where the function test2 is invalid, but it should not affect testing of function test1: export function test1(): boolean { return true; } export function test2(): number { return "1"; } Test: import { assert as Assert } fr ...

An effective method to utilize .map and .reduce for object manipulation resulting in a newly modified map

Here's an example of what the object looks like: informations = { addresses: { 0: {phone: 0}, 1: {phone: 1}, 2: {phone: 2}, 3: {phone: 3}, 4: {phone: 4}, 5: {phone: 5}, }, names: { 0 ...

The ngIf statement in the template isn't functioning properly after a refresh; instead, it is causing a redirection to the homepage

I've been developing with Angular 7, trying to display a <div> ... </div> based on multiple values that I declared as : Boolean = false; in the .ts file. These values are updated in ngOnInit, but for some reason, the page keeps redirecting ...

The image map library functions seamlessly with React but encounters issues when integrated with Next.js

I have been working on a client project that requires the use of an image map. I searched for a suitable library, but struggled to find one that is easy to maintain. However, I came across this particular library that seemed promising. https://github.com/ ...

Retrieving attributes by their names using dots in HTML

Currently working on an Angular 2 website, I am faced with the challenge of displaying data from an object retrieved from the backend. The structure of the object is as follows: { version: 3.0.0, gauges:{ jvm.memory.total.used:{ value: 3546546 }}} The is ...

Patience is necessary as we await the initialization of the lazily loaded

I'm dealing with a scenario where I have a button triggering an onClick handler. In the onClick function, the first action is: if (this.state.lazyLoadedData === undefined) { await this.loadData(); } The issue arises when I click the button rapid ...

Keeping an Rxjs observable alive despite encountering errors by simply ignoring them

I am passing some values to an rxjs pipe and then subscribing to them. If there are any errors, I want to skip them and proceed with the remaining inputs. of('foo', 'bar', 'error', 'bazz', 'nar', 'erro ...

Create a grade array for a group, with each grade containing a nested section array. Within each section, include

I currently have an array of objects where I need to organize each grade into separate arrays. Within each grade array, I want to include the corresponding sections and subjects. The data structure consists of grades, with each grade containing a section ...

Issue: NullInjectorError: R3InjectorError(AuthorModule)[ScrollbarHelper -> ScrollbarHelper -> ScrollbarHelper -> ScrollbarHelper]:

While I am going through a tutorial on the abp framework, I encountered an error with the Author route that says "ERROR Error: Uncaught (in promise): NullInjectorError: R3InjectorError(AuthorModule)[ScrollbarHelper -> ScrollbarHelper -> ScrollbarHelp ...

Analyzing memory consumption by an individual function in a Node.js environment

Our current experiment involves measuring the memory usage of specific functions. Initially, we attempted to utilize process.memoryUsage().heapUsed before and after calling the function, but encountered issues due to the behavior of the garbage collector. ...

Having trouble fixing TypeScript bugs in Visual Studio Code

I am encountering a similar issue as discussed in this solution: Unable to debug Typescript in VSCode Regrettably, the suggested solution does not seem to resolve my problem. Any assistance would be greatly appreciated. My directory structure looks like ...