17

I'm running an express.js application using TypeScript. Every time I try to process request.query.foo I get the following error:

Argument of type 'string | ParsedQs | string[] | ParsedQs[] | undefined' is not assignable to parameter of type 'string'.

Setup:

import { Request, Response } from 'express';

function bar(request: Request, response: Response) {
  const foo: string = request.query.foo; //marked as error
}

I read on the documentation of Express that you can set a configuration called "query parser" that when set to "simple" will always parse the query parameters as a string

The problem is that Typescript still thinks that something other than string or undefined may come and I can't seem to find a way to override the Request interface, I can only extend it.

Is there any way to override the Request interface? is there something wrong in my approach?

2
  • 1
    Please install npm install @types/express Commented Nov 20, 2020 at 14:14
  • @captain-yossarian I did that but I wasn't able to fix the problem. I'm using a third-party custom dependency that has a newer version of express so I had to upgrade. The versions that this dependency uses are 4.16.4 for express and 4.0.37 for the types so that are the ones I added/changed. Also as the project is quite big I would like to find a solution that doesn't require a mass refactor. Commented Nov 23, 2020 at 14:58

4 Answers 4

15

You could define the types that you will expect on the ReqBody, ReqQuery and the other generics of Request type. For the Response you should also be able to define other types and pass it as generics. Don't forget to install @types/express by running npm install -D @types/express

Then you could create other SomeHandlerRequest and others ReqDictionary, ReqBody, ReqQuery and ResBody for each specific case.

import { Request } from 'express'

// if you need define other response generics
// import { Request, Response } from 'express'


// This types could be exported from other file. Then you should import them
type ReqDictionary = {}
type ReqBody = { foo1 ?: string }
type ReqQuery = { foo2 ?: string }
type ResBody = { foo3 ?: string }

type SomeHandlerRequest = Request<ReqDictionary, ResBody, ReqBody, ReqQuery>

const myCustomHandler = (req: SomeHandlerRequest, res) : void => {
   const { foo1 } = req.body
   const { foo2  } = req.query

   // Your custom logic ..... for example...
   if (foo1) {
      console.log("foo1 value = ", foo1)
   }
   if (foo2) {
      console.log("foo2 value = ", foo2)
   }

   res.status(200).json({ foo3 : "it works" })
}

Sign up to request clarification or add additional context in comments.

6 Comments

That could be an option. The problem is that I have a project that was using an older version of express where there was no need to specify the type of query parameter you received. Using your approach would mean to modify all current usages (if I understood correctly). I'll end up using this if I can't find a way to override the existing type and avoid the mass refactor.
@MiguelAlejandroSmurawski Did you find another solution?
@tim.rohrer No, in the end I refactored each use. I think there is no way of telling TS that I did something outside TS's scope so that the actual request object will always be different than the interface declared for it (appart from disabling the validation in that line). I never tried my approach though. I didn't have permission to disable validation so I never got to see if that "query parser" flag actually worked as intended.
I ended up casting as unknown, then as my custom interface type. Not sure if that is a Good Thing, or a Bad Thing.
I'm not sure it is, at least in my current project. I used generics to refine Request, and it just caused problems in other middleware.
|
4

Late to the party and not the best but you can try

const name: string = req.query.name as string;

just fail save the code by null check

1 Comment

a null check is not going to save you if it actually returns a string array. it's just going to blow up somewhere down the line. you can do a typeof check runtime and either discard if not a string, or try to recover (like taking the first/last param from array or actually handling multiple values)
0

Workarounds I ended up using depending on the situation:

1: Example extending the Request type.

import { Request } from 'express';
import { IncomingHttpHeaders } from 'http';

interface iExtendedRequest extends Request {
  headers: IncomingHttpHeaders & { authorization: string };
}

// Use the extended request interface as entry param
const getAuth = (req: iExtendedRequest): string => req.headers?.authorization;

2: Example using double casting:

type Users = {
  id: number;
  name: string;
};

const getUsers = (req: Request) => {
  // Cast req.query as unknown
  const query = req.query as unknown;
  // if allows us to cast it as our type
  const user = query as Users;
  // and then apply destructuring
  const { id, name } = user;

  id // number
  name // string
};

Comments

-6

if you're like me, and due to upgrading a dependency, all of a sudden you had lots of errors, you may want a quick fix (until you go through and type all your requests)

so, simply specify the type of req to any and you're done. back to the old untyped method.

eg,

router.get('/',(req : any, res, next) => { 
    const { foo } = req.query;
    console.log('foo is a string',foo);
 });

Converting an untyped application to a typed one is not a trivial or quick thing. sometimes, you need to upgrade a dependency and get something working again, and have to leave the exercise of adding strict types for another day.

replace all dialog

5 Comments

Replacing all the types by Any completely voids the use of Typescript, so I would not recommend this. It will just disable type checking for all instances of these types and may lead to undefined behavior down the road.
if you read the OP comment, they are facing the issue of an dependency upgrade requiring a new version of express which now enforces types on request (when previously it had not) and a huge refactor/typing effort. stackoverflow.com/questions/64930937/… stackoverflow.com/questions/64930937/…
Still, that's not a solution, that's hiding the issue below the carpet IMO. The quickest way without messing the type inference since the entry point, would be an interface extending the Request type, adding the needed ones and then you can replace (req, for (req: IExtendedRequest instead
you guys don't seem to get it. this is the only negative answer i have on SO, and you guys haven't read the OP's question. he upgraded a dependency and then all of a sudden had 100s of type errors. how long do you reckon it would take to go through and put in the correct types for a legacy application? a day? a week? a month? six months, with QA/tests? keep in mind, the person upgrading may not be the original developer of the application.
see this comment by the OP: stackoverflow.com/questions/64930937/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.