How to type `request.query` in express using TypeScript?

Viewed 20527

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'.
  Type 'undefined' is not assignable to type 'string'.

Setup:

import { Request, Response, Router } from 'express';

const router = Router();

function getHandler(request: Request, response: Response) {
  const { query } = request;

  query.foo; // string | QueryString.ParsedQs | string[] | QueryString.ParsedQs[] | undefined

}

router.route('/')
  .get(getHandler)

Is there a proper way to type request.query without casting?

6 Answers

The solution is to use generics in the following manner.

const router = Router();

interface Foo {
    foo: string;
}

function getHandler(request: Request<{}, {}, {}, Foo>, response: Response) {
  const { query } = request;

  query.foo;

}

router.route('/')
  .get(getHandler)

I like to use the following approach in my projects.

  const { url } = req.query;
  
  if (typeof url !== "string") {
    throw new ServerError("Query param 'url' has to be of type string", 400);
  }

After checking the type TypeScript won't complain any more for this scope.

you can do it like this :-

interface Query {
   param1:string;
   param2:string;
};

function getHandler(request: Request, response: Response) {
   const {param1,param2} = request.query as unknown as Query;
}

Definition

The Request is a generic which accepts additional definitions:

interface Request<
  P = core.ParamsDictionary,
  ResBody = any,
  ReqBody = any,
  ReqQuery = core.Query,
  Locals extends Record<string, any> = Record<string, any>
> extends core.Request<P, ResBody, ReqBody, ReqQuery, Locals> {}

Source: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/express/index.d.ts#L112-L117

Example

interface RequestParams {}

interface ResponseBody {}

interface RequestBody {}

interface RequestQuery {
  foo: string;
}

function getHandler(
  request: Request<RequestParams, ResponseBody, RequestBody, RequestQuery>,
  response: Response
) {
  const { query } = request;

  query.foo; // string
}

In my humble opinion the simpler and straight forward method is to use the StringConstructor. Any downside of this method in this particular situation is welcome in the comments.

var String: StringConstructor (value?: any) => string Allows manipulation and formatting of text strings and determination and location of substrings within strings.

import { Request, Response, Router } from 'express';

const router = Router();

function getHandler(request: Request, response: Response) {
  const { foo } = request.query;

  String(foo) // string
}

router.route('/').get(getHandler)

Another possible solution is to use Type Assertion:

function getHandler(request: Request, response: Response) {

  const foo: string = request.query.foo as string

  //do something with foo ...

}
Related