Expression of type 'string' can't be used to index type 'Request<ParamsDictionary, any, any, Query>' - node.js

I am trying to create a middleware for validating the input data in request.
export function validator(schema: Joi.ObjectSchema, key: string) {
return function (req: Request, res: Response, next: NextFunction): void {
try {
Joi.assert(req[key], schema);
next();
} catch (error) {
console.log(error);
throw new Error(error);
}
};
}
But the req[key] is throwing following error:
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Request'.
No index signature with a parameter of type 'string' was found on type 'Request'.
The other solutions that I found to problems similar to this include the interface in their own code. I don't understand why this error is coming and how to solve this?
Also the following code is from the express's type definition file.
interface Request<P extends core.Params = core.ParamsDictionary, ResBody = any, ReqBody = any, ReqQuery = core.Query> extends core.Request<P, ResBody, ReqBody, ReqQuery> { }
What does the above definition mean?

Changing key: string in the definition of validator to key: keyof Request worked.

Related

How to mock in express with Typescript and Mocha library

How to mock 'Request' in Mocha using express with Typescript?
Current solution is following:
describe("Authorization middleware", () => {
it("Fails when no authorization header", () => {
const req = {
get: () => {
return null;
},
};
expect(isAuth(req as Request, {}, () => {}));
});
});
I have got an error Conversion of type '{ get: () => null; }' to type 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
Is forcing 'unknown' type the only solution to this problem?
You can use node-mocks-http package to create the mockups of the request and response objects for express routing functions.
E.g.
import { expect } from "chai";
import {Request, Response} from 'express';
import httpMocks from 'node-mocks-http';
const isAuth = (req: Request, res: Response) => {
// your code under test
}
describe("Authorization middleware", () => {
it("Fails when no authorization header", () => {
const req = httpMocks.createRequest();
const res = httpMocks.createResponse()
expect(isAuth(req, res));
});
});
The return value of httpMocks.createRequest() API is MockRequest type, its generic parameter is constrained by express Request type. The Request type is a subset of the MockRequest type, so it matches the Request type.

How to add custom properties on the Request object in Express + TypeScript?

I am trying to add a user object as a custom property in the Request object of Express, but I got the following error:
Property 'user' does not exist on type 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>'
This is my code in a middleware function:
// Authenticate person/user through the database.
const person = new Person(username, password);
const user = await authenticate(person); // ORM => read DB
if (!user) {
return res
.status(401)
.json({ message: "Invalid Authentication Credentials" });
}
// attach user to request object
req.user = user; // <= HERE is my problem
next();
How I can add this custom property to the request?
I think the standard way to do this is by extending the Response interface exported by Express and declaring your Data as being part of the Locals generic type.
In #types/express/index.d.ts # line 127:
export interface Response<ResBody = any, Locals extends Record<string, any> = Record<string, any>>
extends core.Response<ResBody, Locals> {}
You can therefore create a Type that will be used in lieu of the default value for the Locals generic like so:
import type { Response, Request, NextFunction } from 'express';
import type { User } from './models'; // Or wherever it is, obviously.
type MyLocals = { user?: User; };
type MyResponse = Response<any, MyLocals>
// Using the `MyResponse` type is as simple as setting the type of `res` to be `MyResponse`, e.g.:
async function doSomeWork (req: Request, res: MyResponse, next: NextFunction): Promise<void> {
console.log(res.locals.user); // undefined | User
}
There are some other ways to achieve this, as well, but this is what I have always done when using Typescript + Express.

Property 'message' does not exist on type 'ErrorRequestHandler<ParamsDictionary, any, any, ParsedQs, Record<string, any>>'.ts(2339)?

Hello I created a custom error middleware handler in my node typescript app
and getting an error that message does not exist on type ErrorRquestHandler
const errorHandler = (
err: ErrorRequestHandler,
_req: Request,
res: Response,
_next: NextFunction
) => {
const statusCode = res.statusCode === 200 ? 500 : res.statusCode;
res.status(statusCode);
res.json({
message: err.message,
stack: err.stack
})
}
I've tried uncommenting "typeRoots" and "types" in my tsconfig.json file and still same error.
What is causing this error?
Thanks
It looks like you have an error handler that is declared correctly but the type of the err parameter is wrong, instead, the const errorHandler itself should be typed with ErrorRequestHandler so the parameters are inferred.
The type ErrorRequestHandler is defined as follows in DefinitelyTyped:
export type ErrorRequestHandler<
P = ParamsDictionary,
ResBody = any,
ReqBody = any,
ReqQuery = ParsedQs,
Locals extends Record<string, any> = Record<string, any>
> = (
err: any,
req: Request<P, ResBody, ReqBody, ReqQuery, Locals>,
res: Response<ResBody, Locals>,
next: NextFunction,
) => void;
Suggesting that your code should look as follows:
const errorHandler: ErrorRequestHandler = (
err,
_req,
res,
_next
) => {
const statusCode = res.statusCode === 200 ? 500 : res.statusCode;
res.status(statusCode);
res.json({
message: err.message,
stack: err.stack
})
}
Note that change to const errorHandler to be typed with ErrorRequestHandler meaning you can drop the explicitly typed parameters on the function (unless you have the tsconfig option enabled that requires them). Do note that now err is typed as err: any thanks to ErrorRequestHandler, you should probably test it to ensure it's an error first before using err.message as its also not guaranteed to be a type of Error. The compiler will allow it however as it's typed as any, to be safe I would recommend you test explicitly for errors using the built-in node utility api before making use of properties.
if (isNativeError(err)) {
// use err.message
} else {
// handle unexpected error
// or send generic unknown error response
}

why request.query is not 'any' anymore? express request.query typescript error

after npm i this is the error that i get if i try to pass query params to a function that expects string:
Argument of type 'string | Query | (string | Query)[]' is not assignable to parameter of type 'string'.
Type 'Query' is not assignable to type 'string'.ts(2345)
import express from "express";
async function getProductsImagesByShopEvent(req: express.Request, res: express.Response,
next: express.NextFunction) {
try {
const params = req.query;
if (!params || !params.shopEventId)
throw new CustomError("params are missing in /business/getProductsImagesByShopEvent", 400, "params are missing");
const shopEvent = new ShopEvent();
const events = await shopEvent.getProductsImagesByShopEvent(params.shopEventId);
res.json(events);
}
catch (error) {
next(error);
}
}
async getProductsImagesByShopEvent(shopEventId: string) {
}
the error is in params.shopEventId..
if i add: const params = (req.query as any); it works
This makes express more strict in typings. You have to add types.
const shopEventId: string = req.query.shopEventId as string

How to fix TypeScript error Property 'isBoom' does not exist on type 'Boom<any> | ResponseObject'

The following source code returns TypeScript errors:
this.hapi.ext({
type: 'onPreResponse',
method: async (request, handler) => {
if (request.response.isBoom && request.response.message !== 'Invalid request payload input') {
if (request.response.isServer) {
logger.captureException(request.response, null, {
digest: this.requestDigest(request)
});
} else {
logger.captureMessage(request.response.message, 'info', null, {
digest: this.requestDigest(request)
});
}
}
return handler.continue;
}
});
Property 'isBoom' does not exist on type 'Boom | ResponseObject'.
Property 'isBoom' does not exist on type 'ResponseObject'.
Property 'isServer' does not exist on type 'Boom |
ResponseObject'. Property 'isServer' does not exist on type
'ResponseObject'.
Argument of type 'string | ((httpMessage: string) => ResponseObject)'
is not assignable to parameter of type 'string'. Type '(httpMessage:
string) => ResponseObject' is not assignable to type 'string'.
How can I fix them? Is there a problem with #types/hapi?
Since the response is a union ResponseObject | Boom | null we can only access common members of a union unless we use a type-guard.
There are several types of type-guards and you ca read more about the here .Below I use an in type guard to discriminated based on the existence of the property
import { Server } from 'hapi';
const hapi = new Server({})
hapi.ext({
type: 'onPreResponse',
method: async (request, handler) => {
if (request.response && 'isBoom' in request.response && request.response.message !== 'Invalid request payload input') {
if (request.response.isServer) {
request.response.message // string
}
return handler.continue;
}
}
});
Since the type Boom is a class an instanceof typeguard should also work:
hapi.ext({
type: 'onPreResponse',
method: async (request, handler) => {
if (request.response && request.response instanceof Boom && request.response.message !== 'Invalid request payload input') {
if (request.response.isServer) {
request.response.message // string
}
return handler.continue;
}
}
});
Note in both cases I added a check for request.response to exclude null from the union. This is only required by the compiler if strictNullChecks are enabled, but might be a good idea anyway.

Resources