What does this error say? Type 'ParsedQs' is not assignable to type 'string'

Viewed 21506

Hello my problem is I want to use the search query of mongoose. But I want to make a get request using a query. Why is that not possible? I do not understand this error. I am using version 5.10.0 of mongoose. I don't want to do it as a post request and I would not like to use req.body. Can anyone help me?

here my code:

export const searching = (req: Request, res: Response) => {
  Company.find({ $text: { $search: req.query } }).exec((err, docs) => {
    if (docs) {
      res.status(200).json(docs)
    } else {
      console.log(err)
    }
  })
}

my error message:

(property) $search: string
No overload matches this call.
The last overload gave the following error.
Type 'ParsedQs' is not assignable to type 'string'.ts(2769)
5 Answers

req.query is an object containing request query

So if you send a request to the endpoint like this /?foo=bar&a=123

You can access the query value from

req.query.foo // bar
req.query.a // 123

You are passing query object to the $search, meanwhile you are supposed to pass a string, so it should be

Company.find({ $text: { $search: req.query.yourQueryKey as string } }).exec((err, docs) => {

Better solution is to type your RequestHandler function

import {RequestHandler} from "express";

type Params = {};
type ResBody = {};
type ReqBody = {};
type ReqQuery = {
    query: string;
}

export const searching: RequestHandler<Params, ResBody, ReqBody, ReqQuery> = (req, res) => {
    const query = req.query.query; // string
}

Even better solution: type your RequestHandler function and use a validation library to validate the request query like joi or celebrate

You are passing req.query to your search and the default Typescript type for req.query is

Request<unknown, unknown, unknown, QueryString.ParsedQs, Record<string, any>>.query: QueryString.ParsedQs 

and if you passed in req.query.searchText the type for that would be

string | QueryString.ParsedQs | string[] | QueryString.ParsedQs[] | undefined

The simple way is already answered here just do

export const searching = (req: Request, res: Response) => { 
const searchText = req.query. searchText as string
  Company.find({ $text: { $search: searchText } }).exec((err, docs) => {
    if (docs) {
      res.status(200).json(docs)
    } else {
      console.log(err)
    }
  })
}

That works but gets sloppy when you have a lot of variables from your query params. In express try out the RequestHandler for your endpoints.

import { RequestHandler } from 'express'

interface QueryTypes {
  searchText: string
  moreSearchText: string
}
export const searching:RequestHandler<unknown, unknown, unknown, QueryTypes > = (req, res) => {
  Company.find({ $text: { $search: req.query } }).exec((err, docs) => {
    if (docs) {
      res.status(200).json(docs)
    } else {
      console.log(err)
    }
  })
}

You can see using the RequestHandler I can pass in a few unknown types and the fourth one I can pass in the query types. I can also remove the Request and Response types to just (req, res) because RequestHandler already types out the handler for us.

You're probably wondering what the 3 previous unknown are and that too in the typescript docs for RequestHandler. Here is the docs.

interface RequestHandler<P = core.ParamsDictionary, ResBody = any, ReqBody = any, ReqQuery = qs.ParsedQs, Locals extends Record<string, any> = Record<string, any>>

Now using RequestHandler. I can type out parameters, body, reqbody or query. Use a combo or skip them using unknown like I did in my example.

This also has the added benefit of giving you type safety so

const test = req.query.test

would highlight a typescript error in vscode for me because 'test' is defined in my QueryTypes interface.

Adding as any like as below might help you -

Company.find({ $text: { $search: req.query as any} }).exec((err, docs) => {
-------------
-------------
-------------

req.query is an object that contains all of query parameters you specified in the query.

If your request looks like this

test.com/search/?s=something

something will be stored in req.query.s.

The second option is to use named route parameters. So you can set do this:

Request URL

test.com/search/something

Route

app.get('/search/:search', (req, res) => {
   console.log(req.params.search); // "something". NB use req.params, not req.query
})

Quick and dirty solution

If your coming here from search, the answer can be quite a bit easier than the others suggested here depending on your use case and specific implementation.

Failure Example:

const foo: string | null = req.query.bar || null 

// Type 'ParsedQs' is not assignable to type 'string' 

Success Example:

const foo: string | null = <string>req.query.bar || null
// No Type errors

Are there risks with this type of inline type definition, yes. If you are certain your values will always be strings, and you're not wanting to dive through all of the Express type definitions and muck up your code with a ton of type definitions so you can simply work with a request variable that is a string and will be a string, then this is probably a good solution for ya.

Related