Cannot invoke an expression whose type lacks a call signature when using node-fetch - node.js

I'm trying to get node-fetch to work in my typescript project:
import * as fetch from 'node-fetch';
import * as assert from 'assert';
export class DatabaseConfigurator {
private url: string;
getNode (): Promise<string> {
return fetch(`${this.url}/_membership`).then((response: fetch.Response) => {
return response.json();
}).then((res: any) => {
assert.equal(res.all_nodes.length, 1);
return res.all_nodes[0];
});
}
}
And I get:
Cannot invoke an expression whose type lacks a call signature. Type 'typeof "/home/vinz243/compactd/node_modules/#types/node-fetch/index"' has no compatible call signatures.
When the definition i installed seems ok (node_modules/#types/node-fetch/index.d.ts):
...
export default function fetch(url: string | Request, init?: RequestInit): Promise<Response>;
My tsconfig.json
{
"compilerOptions": {
"sourceMap": true,
"outDir": "./dist/",
"noImplicitAny": true,
"module": "commonjs",
"target": "es6",
"allowJs": true
},
"include": [
"./src/**/*"
]
}

You've imported the entire module rather than the default export, the fetch function. You're trying to call the entire module as a function which doesn't work.
Instead of
import * as fetch from 'node-fetch';
try
import fetch from 'node-fetch';

Related

Importing Static Functions Typescript

I am trying to import a static class function into my main.js file but using the function after compiling with tsc I get:
TypeError: Cannot read property 'router' of undefined
at Object. (path/to/main.js:36:27)>
I have tried to assign it a type with no luck as well as using different methods of importing again with no luck...
I feel as if the answer is simple but cant seem to locate it in the TS docs and I'm just banging my head at this point.
Here is the class I am trying to export.
Session.ts
export class Session {
//Class Members
constructor() {
//Big constructor lots of this. =
};
//Creating the functions needed
logOut() {
delete Session.ssnsById[this.id];
delete Session.ssnsByCookie[this.authToken];
};
static router = function (req: Request, res: Response, next: Function) {
var cookie = req.cookies[Session.cookieName];
var session = cookie && Session.ssnsByCookie[cookie];
if (session) {
// If the session was last used more than |duration| mS ago..
if (session.lastUsed < new Date().getTime() - Session.duration)
session.logOut();
else {
req.session = session;
}
}
next();
};
}
module.exports = Session;
Importing into my main.ts
import { Session } from "./Session";
// Set up Session on req if available
app.use(Session.router); //<--
After running tsc we get built/main.js
const Session_1 = require("./Session");
// Set up Session on req if available
app.use(Session_1.Session.router); //<-This line throws the error.
Lastly here is my tsconfig.json used to compile
{
"compilerOptions": {
"module": "commonjs",
"esModuleInterop": true,
"target": "es6",
"noImplicitAny": true,
"noImplicitThis": true,
"moduleResolution": "node",
"sourceMap": false,
"baseUrl": ".",
"outDir": "built",
"paths": {
"*": [
"node_modules/*"
]
}
},
"include": [
"Src/**/*"
]
}
1: Please remove module.exports = Session;
2: Add a new file name 'custom.d.ts' in the root directory and add the following:
import { Session } from "./Session";
declare module 'express-serve-static-core' {
interface Request {
router?: typeof Session['router'],
}
}
3: Do this in the file where you imported Session class.
import {Request, Response, NextFunction} from 'express';
import { Session } from "./Session";
// Set up Session on req if available
app.use((req: Request, res: Response, next: NextFunction) => {
req.router = Session.router;
next();
});
You will have your router on the req :)
Removing module.exports = Session; Cleared the problem for me!

TypeScript + Express: Property 'rawBody' does not exist on type 'IncomingMessage'

In my src/app.ts, I have:
import express from 'express';
import bodyParser from 'body-parser';
const app = express()
app.use(bodyParser.json({ verify: (req, res, buf) => req.rawBody = buf }))
But I get the error Property 'rawBody' does not exist on type 'IncomingMessage' on:
app.use(bodyParser.json({ verify: (req, res, buf) => req.rawBody = buf }))
I have a typings/express.d.ts, in which I have:
declare namespace Express {
export interface Request {
rawBody: any;
}
}
and my tsconfig.json is:
{
"compilerOptions": {
"outDir": "./built",
"allowJs": true,
"target": "es6",
"esModuleInterop": true,
"sourceMap": true,
"moduleResolution": "node"
},
"include": [
"./src/**/*"
],
"files": [
"typings/*"
]
}
So what am I doing wrong?
There are two issues here:
1. tsconfig.json
The files option in tsconfig.json doesn't support wildcards like typings/*, only explicit filenames.
You can either specify the full path:
"files": [
"typings/express.d.ts"
]
Or add the wildcard path to include:
"include": [
"./src/**/*",
"typings/*"
]
2. Wrong Type
The error message mentions the type IncomingMessage, however you are augmenting the Request interface instead. Take a look at the type definitions for body-parser (parts omitted):
import * as http from 'http';
// ...
interface Options {
inflate?: boolean;
limit?: number | string;
type?: string | string[] | ((req: http.IncomingMessage) => any);
verify?(req: http.IncomingMessage, res: http.ServerResponse, buf: Buffer, encoding: string): void;
}
The first argument of verify has the type http.IncomingMessage from the 'http' module that's included with Node.js.
To augment the correct type, you'll want to change your .d.ts file to this:
declare module 'http' {
interface IncomingMessage {
rawBody: any;
}
}

In typescript, promisify converts fs.stat to ts error 2349

I am a typescript beginner, I have encountered some typescript warnings when using promisify to convert fs.stas.
const stat: (
pathname: string
) => Promise<fs.Stats | NodeJS.ErrnoException> = util.promisify(fs.stat);
Cannot invoke an expression whose type lacks a call signature. Type 'Stats' has no compatible call signatures.ts(2349)
What does your tsconfig.json and package.json look like?
The following works for me:
import fs from "fs";
import util from "util";
const stat: (pathname: string) => Promise<fs.Stats> = util.promisify(fs.stat);
tsconfig.json:
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"strict": true,
"esModuleInterop": true
}
}
package.json:
{
"dependencies": {
"#types/node": "^11.11.8",
"typescript": "^3.3.4000"
}
}
Also, you shouldn't specify the type of error stat throws in the type of the promise. So instead of Promise<fs.Stats | NodeJS.ErrnoException>, you should just do Promise<fs.Stats>

TypeError: Reflect.hasOwnMetadata is not a function

I am trying to use inversify with typescript and node.js. I am currently using node.js version 6.9.1, typescript version 2.6.2, and ECMAScript 6. When I try to run the node.js application, I keep receiving the following error, "TypeError: Reflect.hasOwnMetadata is not a function".
Following documentation I found online, by adding import "reflect-matadata" at the top of the inversify.config.js file, it seemed to work for awhile, but now the problem is back again.
In my inversify.config.ts file, I have the following code that gets called from my app.ts file:
export let initialize = async (): Promise<Container> => {
var container = new Container();
try {
if (!Reflect.hasOwnMetadata("inversify:paramtypes", ClassToAddInjectableTo)) {
decorate(injectable(), ClassToAddInjectableTo);
}
….
container.bind<interfaces.Controller>(TYPE.Controller).to(MyController1).whenTargetNamed('MyController1');
container.bind<interfaces.Controller>(TYPE.Controller).to(MyController2).whenTargetNamed('MyController2');
} catch (ex) {
throw ex;
}
}
This code exists in app.ts and it calls the code in the inversify.config.ts file:
setup = async () => {
try {
let container: Container = await initialize();
…..
} catch (err){
throw err;
}
}
The problem seems to be on the following line in node_modules/inversify/lib/annotation/decorator_utils.js:22:
if (Reflect.hasOwnMetadata(metadataKey, annotationTarget) === true) {
In the generated inversify.config.js file, the following code seems to be above the import "reflect-metadata":
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
require("reflect-metadata");
I noticed that the help I found online only indicated that I needed to add import "reflect-metadata".
Also, here is my tsconfig.json file:
{
"compilerOptions": {
"module": "commonjs",
"target": "ES6",
"moduleResolution": "node",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"types": ["reflect-metadata"],
"lib": ["ES6"],
"sourceMap": true,
"inlineSources": true,
"pretty": true,
"outDir": "dist",
"rootDir": "src",
"noLib": false,
"declaration": true
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules"
]
}
What could I be missing?
Update
For the issue above, I added the import "reflect-metadata" to the file inversify.config.ts file. However, I commented this import statement and added the import "reflect-metadata" to the app.ts file and now I am getting a different error:
throw new Error(ERRORS_MSGS.DUPLICATED_INJECTABLE_DECORATOR);
^
Error: Cannot apply #injectable decorator multiple times.
Now I found a post on the internet that seemed to describe indicate the adding import "reflect-metadata" adds a Reflect global variable. Also, I don't know if this helps but I removed the #injectable decorator from the controllers.
Try:
npm install reflect-metadata --save
Then import it only once in your entire application:
import "reflect-metadata"
If you are using inversify-express-utils make sure that your controllers are annotated with #controller not with #injectable. Also make sure that your coontrollers are imported once.
import "./controllers/some_controller";
import "./controllers/another_controller";
In case of failure during running jest test, add to the top of the spec file:
import "reflect-metadata";

typescript error running navalia example

I am trying to run this example from https://github.com/joelgriffith/navalia but for the light of me, I couldn't get it to work without error:
navaliatest.ts
/// <reference path="typings.d.ts" />
import { Chrome } from 'navalia';
const chrome = new Chrome();
async function buyItOnAmazon() {
const url = await chrome.goto('https://amazon.com');
const typed = await chrome.type('input', 'Kindle');
const clicked = await chrome.click('.nav-search-submit input');
chrome.done();
console.log(url, typed, clicked); // 'https://www.amazon.com/', true, true
}
buyItOnAmazon();
tsconfig.json
{
"files": [
"navaliatest.ts"
],
"compilerOptions": {
"noImplicitAny": false,
"target": "es6",
"moduleResolution": "node",
"paths": {
"*" : ["/usr/local/lib/node_modules/*"]
}
}
}
typings.d.ts
/// <reference path="/usr/local/lib/node_modules/navalia/build/Chrome.d.ts" />
declare module 'navalia' {
var Chrome: any;
export = Chrome;
}
Below are the versions:
MacBook-Pro:testcasperjs myusername$ node --version
v6.11.2MacBook-Pro:testcasperjs myusername$ npm --version
3.10.10
MacBook-Pro:testcasperjs myusername$ tsc --version
Version 2.4.2
This is the error I got although I do get .js file output:
MacBook-Pro:testcasperjs myusername$ tsc navaliatest.ts
../../../usr/local/lib/node_modules/navalia/node_modules/chrome-launcher/chrome-finder.ts(203,16): error TS2339: Property 'from' does not exist on type 'ArrayConstructor'.
../../../usr/local/lib/node_modules/navalia/node_modules/chrome-launcher/chrome-launcher.ts(99,15): error TS1056: Accessors are only available when targeting ECMAScript 5 and higher.
navaliatest.ts(3,10): error TS2305: Module ''navalia'' has no exported member 'Chrome'.
I am sure there is a stupid mistake somewhere but please could someone help me and take a look? Thanks.
You don't need to redeclare navalia. It has already been done for you at node_modules/navalia/build/index.d.ts given that moduleResolution is set to Node
You'll need to set module to commonjs so that you can run it in node
tsconfig.json
{
"files": [
"navaliatest.ts"
],
"compilerOptions": {
"noImplicitAny": false,
"target": "es6",
"module": "commonjs",
"moduleResolution": "Node"
}
}
navaliatest.ts (No change)
import { Chrome } from 'navalia';
const chrome = new Chrome();
async function buyItOnAmazon() {
const url = await chrome.goto('https://amazon.com');
const typed = await chrome.type('input', 'Kindle');
const clicked = await chrome.click('.nav-search-submit input');
chrome.done();
console.log(url, typed, clicked); // 'https://www.amazon.com/', true, true
}
buyItOnAmazon();
It'll create navaliatest.js with no errors, which can be run in node.

Resources