Typescript syntax related - node.js

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".

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

TS/Node.js: Getting the absolute path of the class instance rather than the class itself

Is there a way to get the path (__dirname) of the file where an instance of a class was made without passing that into the constructor?
For example,
// src/classes/A.ts
export class A {
private instanceDirname: string;
constructor() {
this.instanceDirname = ??
}
}
// src/index.ts
import { A } from "./classes/A"
const a = new A();
// a.instanceDirname === __dirname ✓
I tried callsite, it worked but I had to do some regex that I'm not happy with to get what I need, I also tried a module called caller-callsite, but that ended up returning the module path, not the path of the file where the instance was made.
Is there a workaround for this?
I would have callers pass in the location information. Sniffing this stuff seems like a code smell to me (pardon the pun). ;-)
But you can do it, by using regular expressions on the V8 call stack from an Error instance, but it still involves doing regular expressions (which you didn't like with callsite), though it's doing them on V8's own stacks, which aren't likely to change in a breaking way (and certainly won't except when you do upgrades of Node.js, so it's easy to test). See comments:
// A regular expression to look for lines in this file (A.ts / A.js)
const rexThisFile = /\bA\.[tj]s:/i;
// Your class
export class A {
constructor() {
// Get a stack trace, break into lines -- this is V8, we can rely on the format
const stackLines = (new Error().stack).split(/\r\n|\r|\n/);
// Find the first line that doesn't reference this file
const line = stackLines.find((line, index) => index > 0 && !rexThisFile.test(line));
if (line) {
// Found it, extract the directory from it
const instanceOfDirName = line.replace(/^\s*at\s*/, "")
.replace(/\w+\.[tj]s[:\d]+$/, "")
.replace(/^file:\/\//, "");
console.log(`instanceOfDirName = "${instanceOfDirName}"`);
}
}
}
Those three replaces can be combined:
const instanceOfDirName = line.replace(/(?:^\s*at\s*(?:file:\/\/)?)|(?:\w+\.[tj]s[:\d]+$)/g, "");
...but I left them separate for clarity; not going to make any performance difference to care about.

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

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.

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.

Declaration merging and aliasing not working together

I recently started converting my node.js/express application to typescript.
It is working great so far, but there are some things I am not sure of:
I noticed that in the sample project by Microsoft, they are inconsistent with their typing.
app.ts
app.get('/findImages', function(req, res) {
// ...
}
routes/index.ts
export function index(req: express.Request, res: express.Response) {
// ....
};
As you can see they are sometimes defining types, sometimes not. I noticed that there is no IntelliSense in the first case, but this might also be an issue of IntelliJ. What would be the best practice here?
At first I thought I would just type everything, but then I noticed another odd behaviour:
app.ts
/// <reference path="connect-flash/connect-flash.d.ts" />
/// <reference path="express-session/express-session.d.ts" />
/// <reference path="express/express.d.ts" />
/// <reference path="passport/passport.d.ts" />
import express = require('express');
import session = require('express-session');
import passport = require('passport');
import flash = require('connect-flash');
var app: express.Express = express();
// Express without capital letter
route.get('/', function (req: express.Request, res: express.Response): void {
req.flash('message'); // no IntelliSense
var session = req.session; // no IntelliSense
var ip = req.ip; // works
var test = req.params.test; // works
});
// Express with capital letter
route.get('/', function (req: Express.Request, res: Express.Response): void {
req.flash('message'); // works
var session = req.session; // works
var ip = req.ip; // no IntelliSense; compile error TS2339
var test = req.params.test; // no IntelliSense; compile error TS2339
});
I am getting the following errors:
Error:(54, 22) TS2339: Property 'ip' does not exist on type 'Request'.
Error:(55, 24) TS2339: Property 'params' does not exist on type 'Request'.
I was looking through the definition files and noticed that there are apparently two different ways to define a module:
express.d.ts
declare module Express {...}
declare module "express" {...}
I tried multiple combinations of upper and lower case (also in the other definition files) with no success.
It seems that there are two separate definitions of the express module. The first one is also present in other modules such as express-session.d.ts or connect-flash.d.ts, and they are merged together correctly. But there seems to be a problem with the upper and lower case difference in the express.d.ts file. Is there a way to merge them?
Thanks for your help.
As you can see they are sometimes defining types, sometimes not. I noticed that there is no IntelliSense in the first case, but this might also be an issue of IntelliJ. What would be the best practice here
I try to be explicit everywhere. But you can avoid this if typescript can infer it for you.
I am getting the following errors:
If ip is a part of the express request then that's just an omission in the express.d.ts that needs to be fixed.
noticed that there are apparently two different ways to define a module:
These the two different kind of modules. declare module foo means that is a global variable foo available. declare module "foo" means you can do import foo = require('foo').
But there seems to be a problem with the upper and lower case difference in the express.d.ts file. Is there a way to merge them
They should all have the same case.

Resources