What steps can I take to ensure TypeScript compiler approves of variance in calling generic handlers, such as those used in expressJS middleware?

disclaimer: I am a bit uncertain about variance in general...

Here is the scenario I am facing:

// index.ts
import express from 'express';
import {Request, Response} from 'express';
const app = express();
app.use(handler);

interface BetterRequest extends Request {
    foo: string;
}

function handler(req: BetterRequest, res: Response) {
    req.foo = 'bar';
}

// tsconfig.json
{
    "compilerOptions": {
        "target": "es6",
        "module": "commonjs",
        "strict": true,
        "allowSyntheticDefaultImports": true,
        "esModuleInterop": true
    }
}

The error message I encounter is

[ts]
Argument of type '(req: BetterRequest, res:Response) => void' is not assignable to parameter of type'RequestHandlerParams'.
Type'(req: BetterRequest, res: Response) => void' is not assignabl...
   <!-- Here the content was shortened -->

<p>I comprehend the meaning of the error and that I could either disable those warnings through editing the <code>tsconfig.json file or by using a line comment. However, that's not my preferred solution.

What would be the correct way to address this issue?

Is the below approach the only option?

// index.ts
function handle(req: Request, res: Response) {
    const modifiedReq = req as BetterRequest;
    modifiedReq.foo = 'bar';
}

Answer №1

If you are certain that you will be setting the foo property right away and want to avoid constantly checking if it is defined, an explicit cast is the most efficient way to go. Alternatively, you could declare the foo property as optional. This allows a Request to be implicitly converted to a BetterRequest, making your handler compatible with an Express handler. However, keep in mind that the type of the foo property will include undefined, requiring you to handle this scenario every time you access the property.

Answer №2

An issue arises at the point of app.use(handler) due to the error message received:

The type 'Request' cannot be assigned to type 'BetterRequest'. The property 'foo' is missing in type 'Request'.

This assignment is rejected by the compiler because when handler says, "I'm expecting a 'BetterRequest' from the caller," app.use() counteracts with, "I can only promise to pass in a 'Request' for any handler provided."

To resolve this, using a type assertion seems like the most elegant solution:

app.use(handler as RequestHandler)

With this type assertion, app.use() acknowledges that even though handler is a 'BetterRequestHandler' in reality, it will function correctly as a 'RequestHandler'. This eliminates the need to constantly verify the value of req.foo just to satisfy the compiler.

This approach builds upon Matt McCutchen's proposal of making req.foo optional, ensuring smoother operation without continuously validating its existence.


For instance, in my scenario, I aimed to incorporate a validation middleware before the primary handler:

interface ValidatedRequest<T extends object> extends Request {
    validation: T,
}

function registerValidate(
    req: ValidatedRequest<RegisterInput>,
    res: Response,
    next: NextFunction,
) {
    const validation : string | RegisterInput = validate<RegisterInput>(req.body);
    if (typeof validation === 'string') {
        return next(badRequest(validation));
    } else {
        req.validation = validation;
        next();
    }
}

function register(
    req: ValidatedRequest<RegisterInput>,
    res: Response,
    next: NextFunction
) {
    // Use req.validation
}

app.post(
    '/path', 
    registerValidate as RequestHandler,
    register as RequestHandler
);

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

Why does my Node.JS Express request return undefined after submitting a post request from a form?

Having trouble retrieving data from an HTML form using Node.JS/Express. The req.body seems to be empty, returning an undefined value. Node const express = require('express') const bodyParser = require('body-parser') const app = express ...

Showcasing data from MongoDB to the user interface

I have a nested array stored in my MongoDB database that I need to showcase on the frontend using Jade and Express. Despite my efforts, I am facing difficulties in properly displaying these items: Below is the nested data I aim to present: MongoDB: { ...

To handle async actions in Typescript with React and Redux, ensure that all actions passed to axios are plain objects. If you need to perform

Looking for assistance with Typescript, React, and Redux. export function fetchAllMeals (subject: string){ axios .get(`https://www.themealdb.com/api/json/v1/1/search.php?s=${subject}`) .then((response: any) => { console.log(response.data) ...

Creating unique random shapes within a larger shape on a canvas, as shown in the image

I have a parent rectangle and would like to add up to 10 or fewer rectangles on the right-hand side corner of the parent rectangle, as shown in the image below: I attempted to write code to achieve this, but the alignment is off-center from the parent rec ...

Redirecting a React page to a SAML login using an Express backend with Passport integration

I have successfully built an application in Express, but I am now transitioning to React, with express serving as an API. My current challenge involves migrating the login page, particularly when it comes to authentication methods: Implementing Passport f ...

What is the best way to pass an array as a parameter in Angular?

I have set up my routing module like this: const routes: Routes = [ { path: "engineering/:branches", component: BranchesTabsComponent }, { path: "humanities/:branches", component: BranchesTabsComponent }, ]; In the main-contin ...

TS2531: Nullability detected in object when using .match() method

I'm encountering a linting error on fileNameMatches[0] in the following code snippet. Strangely, the error doesn't appear on the Boolean() check. Even if I remove that check, the issue remains unresolved. Can anyone suggest a solution? protected ...

Collection of assorted objects with varying sizes that are all subclasses of a common superclass

I need to create an array that can hold any number of items, all of which are subclasses of the same base class. The issue I'm facing is with type checking in TypeScript - when I declare the array as Array<BaseClass>, I have to cast each item to ...

Exploring the benefits of using getServerSideProps with NextJS and Typescript for

Dear community, I am facing a challenge with integrating NextJS Layout and Typescript. Although everything seems to be working fine, I can't seem to get rid of the squiggly line under the props when passing them from getServerSideProps. The prop {som ...

Achieve validation of numerous variables without the need for a string of if-else

If we have three variables, such as firstName, lastName, and email, how can we check if they are empty or not without using multiple if else blocks? let firstName = "John"; let lastName = "Doe"; let email = "john.doe@example.com"; if (firstName.trim() == ...

What causes the (passport) middleware to trigger when using app.get, but not when using app.post?

Here is the middleware function I am working with: function isLoggedIn(req, res, next) { if (req.isAuthenticated()) { console.log('***User is logged in***'); next(); } else { res.redirect('/'); console.log('* ...

Update the nest-cli.json configuration to ensure that non-TypeScript files are included in the dist directory

I've been searching for a solution for hours now: I'm developing an email service using nestJS and nest mailer. Everything was working fine until I tried to include a template in my emails. These templates are hbs files located in src/mail/templ ...

After being awaited recursively, the resolved promise does not perform any actions

When working with the Twitter API, I need to make recursive method calls to retrieve tweets since each request only returns a maximum of 100 tweets. The process is straightforward: Call the function and await it Make an HTTP request and await that If the ...

An issue encountered with the 'sharp' module in Node.js Express

Hey there, I encountered an error in my Node.js project as shown below. If anyone has a solution, please let me know. A technical error has occurred. Error: Cannot find module 'sharp' at Function.Module._resolveFilename (module.js:536:15) ... at ...

There appears to be an issue with the compilation of the TypeScript "import { myVar }" syntax in a Node/CommonJS/ES5 application

In my Node application, I have a configuration file that exports some settings as an object like this: // config.js export var config = { serverPort: 8080 } Within another module, I import these settings and try to access the serverPort property: // ...

Having trouble with my findIndex function in Node.js while working with a mongo DB database

I am having difficulty finding the index of a specific product in a MongoDB database. const cart = await this.model.findOne({ user: { $eq: user } }); if (cart) { const itemFound = cart.products.findIndex( (item) => item._id === ...

Error in Angular SSR: Build failed due to project reference without "composite" setting set to true

Currently facing an issue while developing an Angular App with SSR. When using npm run build:ssr, the following errors are displayed: ERROR in [...]/tsconfig.json [tsl] ERROR TS6306: Referenced project '[...]/tsconfig.app.json' must have se ...

How come when I remove the line app.use(express.static(__dirname, 'public')) my HTML pages don't load the CSS files?

Currently, I am in the process of learning about NodeJS and Express. Recently, I used the "express" command to create the initial structure for a project. However, one concept that is confusing me is the following line of code: app.use(express.static(path ...

Simple steps for Mocking an API call (Get Todos) using ComponentDidMount in React with Typescript, Jest, and Enzyme

About the application This application serves as a basic To Do List. It retrieves tasks from an API located at https://jsonplaceholder.typicode.com/todos?&_limit=5. Objective of the project The main goal is to test an API call that triggers ...

Error: Failed to fetch the data from the Firebase database

I have recently added an edit button to my product list, but when I click on it, the form page opens with no data populated. Upon debugging in my product.service.ts file, I noticed that it outputs null when using console.log(p). I believe this is where the ...