#TS2339: Error extending Express Request (e.g. req.payload)

23 messages · Page 1 of 1 (latest)

brazen meadow
#

Hi,

I am currently migrating a Express app to TypeScript. I have some middleware that needs to store info in the request object (e.g. req.payload) but I keep getting an error.

Here's a simplified version of my code:

// server.ts

import { Application } from 'express';
import { Request, Response, NextFunction } from 'express';

import express from 'express';
const app: Application = express();

function middleware1(req: Request, res: Response, next: NextFunction) {
  req.payload = {
    name: 'bob',
  };

  next();
}

app.use(middleware1);

app.get('/', (req, res, next) => {
  res.json({ message: 'hello world' });
});

const PORT = process.env.PORT || 5005;

app.listen(PORT, () => {
  console.log(`Server listening on port http://localhost:${PORT}`);
});

If I try to store something in req.payload, TypeScript will complain:

Property 'payload' does not exist on type 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>'.(2339)

so I'm trying to extend the Request interface from Express by following this article.

I have created the file @types/express/index.d.ts with the following content:

// @types/express/index.d.ts

declare global {
  namespace Express {
    interface Request {
      payload?: {};
    }
  }
}

And I've updated tsconfig.json to include the following:


// tsconfig.json

// ...

    "typeRoots": [
      "@types",
      "./node_modules/@types"
    ]

However, I still get the error.

Am I missing something? Any help is really appreciated 🙏

outer vigil
#

I haven't used Express in a long while and it seems like things have changed somewhat?
Regardless, here's something that works:

empty flintBOT
#
nonspicyburrito#0

Preview:```ts
import {Application} from "express"

declare module "express-serve-static-core" {
interface Request {
payload: {
name: string
}
}
}

declare const app: Application

app.use(req => {
req.payload = {
name: "bob
...```

outer vigil
#

Notably:

  • You don't need to (and should not) use a .d.ts file, just have your declaration merging in a regular .ts file.
  • Request seems to be located in module express-serve-static-core and extends Express.Request. I don't remember this being the case and not sure if there's any doc to say which one you should merge into, so I just did it with the former.
#

If it turns out merging into the former is wrong, then you can change it to merge into the latter instead.

brazen meadow
#

hi Burrito, thanks a lot for your answer!

I've tried putting the interface in the same .ts file (eg. server.ts) with this code:

namespace Express {
  export interface Request {
    payload?: {};
  }
}

function middleware1(req: Express.Request, res: Response, next: NextFunction) {
  req.payload = { name: 'bob' };
  next();
}

app.use(middleware1);

That seems to work up to a point... now it seems I can store things in req.payload but not in req.foobar, which is exactly what I'm trying to do.... however, it gives a new different error (screenshot)

Do you have any suggestion? I really lost here

outer vigil
#

That's a different and unrelated error.

#

If you look at the signature of Application#use, it says that next is optional, so you need to change your middleware's next to also be optional.

#

Your declaration merging is still wrong btw, if you want to merge into Express.Request you should do this:

declare global {
    namespace Express {
        interface Request {
            payload: ...
        }
    }
}
brazen meadow
#

I trying to move that declaration to a separate file and I wonder the following:

a) Aren't this type of declarations not supposed to go into a "d.ts" file (since they provide declarations for a module)?

b) What would be the correct syntax to extract this to a separate file? (I've tried the patterns from some articles and also the code in the screenshot, without much success)

I have something that kind of works but further help or advice is appreciated, if you have time of course.

outer vigil
#

.ts = .js + .d.ts, so whatever you can do with .d.ts you can also do it with just .ts.
The reason writing .d.ts is not recommended is because it conflicts with usage of skipLibCheck, which skips type checking .d.ts files. Because your dependencies (like Express) also have their own .d.ts files, but checking them is a waste of time and slows down your compilation for no reason, it's recommended to turn on skipLibCheck. However, if you also write code in .d.ts then they also won't get checked. That's why it's recommended to turn on skipLibCheck and don't write .d.ts, so you get the best of both worlds.

stable fox
#

@types/express creates a global interface Express with Request, Response etc on it. These are empty interfaces that you can extend with your own properties.

These global interfaces are then merged into the Request and Response types that are exported from 'express'. The exported Request, Response are different to the global Express.Request / Express.Response. And these are different to the global Request / Response which seem to be related to fetch.

So you need to declare Express.Request / Express.Response to add properties, but use Express / Request from the 'express' package to type your callback.

#

Technically you could probably declare the express exported types to add properties there, but that doesn't seem to be the intended approach

stable fox
#

TS will scan it and modify global types, it doesn't have to be imported anywhere.

brazen meadow
#

thanks a lot both!

I finally got this working with this configuration,

src@types\express\index.d.ts:

declare namespace Express {
    interface Request {
        payload?: {};
    }
}

Plus, adding this to tsconfig.json:

"files": ["src/@types/express/index.d.ts"]

I guess the main part that I was missing was updating tsconfig, apparently that's needed (at least with ts-node-dev).

Again, thanks to both, that was really helpful!!

#

!resolved

stable fox
#
// src/Express.Request.ts

declare global {
  namespace Express {
    interface Request {
      payload: {}
    }
  }
}
#

All you need is that block in a .ts file