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

5

15

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)
Compel answered 25/8, 2020 at 13:24 Comment(0)
N
25

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

Neptune answered 25/8, 2020 at 13:43 Comment(4)
Yes but the problem is that even if I type req.query.foo or something like that I get the same error. This: No overload matches this call. The last overload gave the following error. Type 'string | ParsedQs | string[] | ParsedQs[] | undefined' is not assignable to type 'string'.Compel
Check my edit. You need to cast the type to string using asNeptune
Does this solution take into account that multiple equal query parameters in the URL query, for instance "demo.com/path?id=first&id=second", would result in req.query.id being an array of ParsedQs or strings, like ["first", "second"]?Gouge
Thank you @Neptune for recommanding celebrate module in addition to RequestHandler function: it is a very powerful combo!Babbage
P
5

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.

Paring answered 1/11, 2021 at 22:12 Comment(6)
I found that interfaces make Express complain if I pass more than one middleware (because they don't have the same RequestHandler types). I didn't get the same error with type declarations.Nadabas
for your middleware you can always reset the RequestHandler<any, any, any, any> or replace any with unknown.Paring
Thank you. Any idea why this problem doesn't appear when you use type instead of interface?Nadabas
when you use type it uses the type only within the current handler. It doesn't change the handler signature like the generic does.Paring
where QueryString is coming from?Forevermore
it's defined by express typesParing
O
2

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.

Ostend answered 12/7, 2022 at 21:4 Comment(0)
T
1

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
})
Tranquillize answered 25/8, 2020 at 13:43 Comment(0)
Y
0

you can simply do something like that:

export const createStory = catchAsync(async (req: Request, res: Response) => {
const result = await userCreateBookService.create(req.body, req.query);

export function create(payload: UserStory, query: { teamOwner?: string }) {
Yarvis answered 2/7 at 10:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.