error TS2749: 'IRequestWithUser' refers to a value, but is being used as a type here. Did you mean 'typeof IRequestWithUser'? - node.js

I am extending Request in express library to contain a user property:
import { Request } from 'express';
export default interface RequestWithUser extends Request {
user: {
user_name: string;
password: string;
}
}
The title error appears in the first parameter annotation:
import IRequestWithUser from '../shared/interfaces/isRequestWithUser';
const router: Router = Router();
router.post('/myRoute', async (req: IRequestWithUser, res: Response) => {
/*
error TS2749: 'IRequestWithUser' refers to a value,
but is being used as a type here. Did you mean
'typeof IRequestWithUser'?
*/
I don't believe interfaces are values. They should purely be types. So what is causing this error?
Also tried
typeof IRequestWithUser This results in No overload matches this call

Perhaps there's something missing from your code snippets, but the only error I get on Typescript 3.9 is one that relates to the overload error that you allude to later in your question.
Because the .post() call is looking for a callback that expects type Request, you can't type your req argument as IRequestWithUser. This is because, although IRequestWithUser will contain all properties of Request, the Typescript compiler can't guarantee that the extra properties you plan to have in req will be there when the callback is run.
The recommended way to extend Express types is to use interface merging. This allows you to "redefine" the Request type altogether, but it's a global definition so it gets messy:
// definitions/express/index.d.ts
import * as express from 'express' // Unfortunately, you need some kind of import here to make this a valid module.
declare module "express-serve-static-core" {
export interface Request {
user: {
user_name: string;
password: string;
}
}
}
Your compiler should pick this file up on its own and support your new property using the original Request type. You might need to have an explicit typeRoots set in your tsconfig.json if you're using something like ts-node though.

Related

Basic question about typescript annotation and node packages

I'm new to typescript and understand the basics of it. But I'm getting a bit confused about best practices when it comes to node packages, annotations and describing the types of these when using packages in my project.
I mean, is it really needed to describe a package's return annotations, or specify data type in their parameters which are used by the packages when they are already defined with the #type downloaded?
Lets take an example of the mysql2 package.
`
const poolFunction = async (SQL: string): Promise < [RowDataPacket[] | RowDataPacket[][] | OkPacket | OkPacket[] | ResultSetHeader, FieldPacket[]] > => {
return await pool.query(SQL);
}
`
Or express...
`
const routeFunction = (req: Request, res: Response): void => {
}
`
Of course it's very descriptive, but is it needed?
Just trying to understand and hopefully typescript in the future!
is it really needed to describe a package's return annotations, or specify data type in their parameters which are used by the packages when they are already defined with the #type downloaded?
In your examples yes, because TypeScript can't know if routeFunction is going to be used with some Express function. By isolating it you must type the parameters, although the return type can be inferred.
TypeScript will infer the types of the parameters if you send the expected function, for example:
// no need to type app because TS already knows
// the return type of express()
const app = express();
// no need to type request or response because TS already knows
// that .get() expects a function of 2 parameters, a Request and a Response
app.get('', (request, response) => {});
// since routeFunction is declared outside an Express context,
// TS can't know that request: Request and response: Response,
// this function can be used anywhere not only for Express
function routeFunction(request, response) {} // error

How can I use packages that extend `koa.Request` in TypeScript?

I am trying to use koa-tree-router and koa-bodyparser at the same time, but I keep getting TypeScript errors:
export const userLoggingRouter = new KoaTreeRouter<any, DefaultContext>();
userLoggingRouter.post('/logs/action', (ctx) => {
const logEntries = ctx.request.body;
const user = ctx.state.user;
// ...
});
error TS2339: Property 'body' does not exist on type 'Request'.
I have #types/koa-bodyparser installed, and it contains the following definition:
import * as Koa from 'koa';
declare module 'koa' {
interface Request {
body: string | Record<string, unknown>;
rawBody: string;
}
}
But it doesn't seem to do anything. I found this question, but importing koa-bodyparser directly also does not do anything. How do I get TypeScript to recognize the extended Request type?
Edit: Creating a .d.ts file inside my project containing the following:
import {Request} from "koa";
declare module "koa" {
interface Request {
body: any;
}
}
Made the compile error go away, but this seems like an inelegant solution because I would have to copy over type information for every package that modifies koa.Request.
This was happening because I was using Yarn PnP and I had two different versions of #types/koa installed. Once I added a resolutions field to my package.json that forced all of the other TypeScript definitions to use the same version of #types/koa, everything worked.

Typescript syntax related

I am starting to develop applications using typescript. I had come across a code snippet where I couldn't understand the line marked within ** <> **. Anybody please throw some light.
export const applyRoutes = (routes: Route[], router: Router) => {
for (const route of routes) {
const { method, path, handler } = route;
**(router as any)[method](path, handler);**
}
};
Regards,
Karthikeyan R
(router as any) tells typescript that regardless of what it thinks the types are, it should treat router as having type any. In other words, it turns off type checking.
router[method](path, handler) means "access the method property on router, then call it passing in path and handler".

ts-check error: property does not exist on type

I have a node code base and I want to migrate it to typescript. So the first thing I did was to add //#ts-check and fix the issues that typescript had. I could solve all the issues except for this one.
I need the client's ip address for the purpose of blacklisting clients that send too many redundant requests. So I use the middleware below to add the client's ip address to the request object.
app.use((request, _, next) => {
const ip = request.get('x-real-ip')
if (ip) {
Object.defineProperties(request, { clientIP: { value: ip, writable: false } })
} else {
Object.defineProperties(request, { clientIP: { value:request.socket.remoteAddress, writable: false } })
}
next()
})
But when I want to access the clientIP property elsewhere, typescript gives the error below:
Property 'clientIP' does not exist on type 'Request'.
What should I do to make this error go away?
Thanks
You'll need to augment the Request interface in the express-serve-static-core module with the new property. (According to the typings, the express module has a Request interface that extends the one in express-serve-static-core, but augmenting this one will not cover all uses.) Put the following in a .d.ts file in your project:
declare module "dummy" {
module "express-serve-static-core" {
interface Request {
clientIP: string;
}
}
}
The outer module declaration is needed to make the inner module declaration an "augmentation" instead of shadowing the original module, as mentioned in this thread. That trick is unfortunately not properly documented AFAIK.

Using express-validator typescript definitions

I'm trying to convert some of my code to TypeScript, but having problems with express-validator definitions
My code looks like this:
///<reference path='../../../d.ts/node.d.ts' />
///<reference path='../../../d.ts/express.d.ts' />
///<reference path='../../../d.ts/express-validator.d.ts' />
import express = require('express');
import validator = require('express-validator');
function validate(req: express.Request, res: express.Response) {
req.assert('name', 'Name is required').notEmpty();
var errors = req.validationErrors();
if (errors !== null && errors.length > 0) {
res.json({
result: false,
errors: errors
});
return false;
}
return true;
}
Typescript compiler generates following errors:
error TS2094: The property 'assert' does not exist on value of type 'express.Request'.
error TS2094: The property 'validationErrors' does not exist on value of type 'express.Request'.
Which makes sense especially looking at the expreess-validator definition
export interface RequestValidation {
check(field: string, message: string): Validator;
assert(field: string, message: string): Validator;
sanitize(field: string): Sanitizer;
onValidationError(func: (msg: string) => void): void;
}
My understandment of RequestValidation interface is it must extend express.Request interface, but modifying this interface declaration does not really help.
I'm I doing something wrong?
Thank You!
It looks to me like the Express Validator library extends the Express Request object. i.e. it adds additional methods to the existing Request defined in Express.
Disclaimer: I haven't been able to find any good documentation for the Express Validator library and if someone has a link I can be more exact.
With this in mind, if the Express Validator library extends the Express Request interface, the definition should reflect this. Here is an example that extends the definitions for Express and Express Validator on Definitely Typed.
declare module Express {
interface Request extends ExpressValidator.RequestValidation {
}
}
This will solve the issues with assert, for example - if someone finds some documentation I expect the validatonErrors issue can be solved in a similar way.
I have a helper function that checks for the 'id' param on the request object. The declaration that works correctly on TypeScript 1.5.0-beta is:
// Somewhere at the top of your TypeScript code
/// <reference path="../../../../typings/tsd.d.ts" />
import ExpressValidator = require('express-validator');
import util = require('util');
And the helper function that I use:
function getOnePreconditions(req:ExpressValidator.RequestValidation, res:express.Response, next:Function) {
req.checkParams('id', 'Parameter Id is mandatory').notEmpty().isInt();
var errors = req.validationErrors();
if (errors) {
res.send(400, 'errors' + util.inspect(errors));
} else {
next();
}
}
If you do have tsd.json defined for the project and you are correctly referencing to the *.d.ts TypeScript definition files in your code, then definitely add the declaration file for express-validator by using:
tsd install express-validator --save

Resources