Typescript error: This expression is not callable. Type 'typeof import("koa-session")' has no call signatures - node.js

Partially solved:
There is an old issue on github where this problem is described a bit. When you declare a module in a global scope, it rewrites the whole exported module's types. When you declare a module inside a module, it merges. Why? I have no idea
https://github.com/microsoft/TypeScript/issues/17736#issuecomment-344353174
I want to extend third party module's type by interface merging. Everything works fine, but when I comment
export {}; in types.d.ts I encounter the following error:
This expression is not callable. Type 'typeof import("koa-session")'
has no call signatures
Could you explain why it happens?
You can check the working code here:
https://codesandbox.io/s/typescript-node-nocfq?file=/src/types.d.ts

A similar problem has been addressed on TypeScript github. Unfortunately I am now aware of any other documentation page that would describe it.
Commenting out the export {} turns the types.d.ts file from a module into a script. From TypeScript handbook:
In TypeScript, just as in ECMAScript 2015, any file containing a top-level import or export is considered a module. Conversely, a file without any top-level import or export declarations is treated as a script whose contents are available in the global scope (and therefore to modules as well).
Since the file without export statement (and without import statement) is not a module but rather a script it has no information about any modules and will indeed disregard the fact that there is an existing definition for "koa-session".
You can try this in your sandbox - adding any top-level import or export in types.d.ts (it can be completely unused) will fix the This expression is not callable error.

You need to import the interface if you want to enhance it and do interface merging. What you're doing is rewriting it altogether.
import Session from "koa-session";
declare module "koa-session" {
interface Session {
user: {
id: number;
username: string;
};
}
}
Just do this and you will enhance the interface just as you want.

Related

Instantiate a class knowing the file path at runtime

How can I instantiate a class (with, say, a known empty constructor), for example:
at api/EmptyClass1.ts, I have:
export default class EmptyClass1 {
}
and, at api/EmptyClass2.ts, I have:
export default class EmptyClass2 {
}
I want this function:
function(filepath:string):any{
return Object.fromFile(filepath); //this line is mock code
}
to return a new instance of either EmptyClass1 or EmptyClass2, if the parameter filepath:string is "api/EmptyClass1.ts" or "api/EmptyClass2.ts", respectively.
The files defining the classes may not be known at the time the function is written may include any number of files. Consequently, using the import statement for each class, then using a switch, or if-then statements is not an acceptable solution.
The .ts files defining the classes are transcoded to javascript and reside in the application .build folder as .js files.
I am using typescript on node.js (recent versions).
Use require instead, and your problem will be solved. If the file may not exist, you can use optional-require if you want to have a fallback without using try/catch.
function fromFile(filepath:string):any{
// return Object.fromFile(filepath); //this line is mock code
return require(filepath);
}
Or just call require directly instead of wrapping it in another function.
Also check:
nodejs require inside TypeScript file

module augmentation on node module that exports class: how to add instance property?

Update: I found exactly the same question on Reddit. Still looking for an answer though, since on Reddit, only a hack in node_modules/#types/ws/index.d.ts is proposed, which I rather not touch for obvious reasons.
I'm using the ws node module to create a socket server. Its typings are of the following structure:
declare class WebSocket extends events.EventEmitter {
// ...
}
declare namespace WebSocket {
// ...
}
export = WebSocket;
What is the correct way of extending the typings so that I can add an instance property id to a WebSocket class? I tried the following in a regular typescript(.ts) file:
class WebSocketServerWithId extends WebSocket {
id: number;
}
Which returns [ts] Type 'typeof WebSocket' is not a constructor function type. [2507]. I've also tried to use the approach in this Stackoverflow question with the same error as a result. I've read the documentation on declaration merging but still am confused.
Anyone knows hows to add an instance property to a class defined and exported in an external node module? (written in commonjs, meant to be executed in node alone).

Can't import exported functions

I am having strange issues with Typescript when I import things from a file which exports them. Sometimes I will export a function, then import it to another file, then I use the function and it is not a function anymore. When I define the function in the same file, all of a sudden the function is a function?!?!?
Why would a function stop being a function when it is exported? I have had similar problems with classes too.
The hard part of this issue is I can't recreate a simple example because it only happens when I am using some kind of higher level package.
For example, I had a similar issue with sequelize-typescript here: my github issue with typescript-sequelize
Below is some codes showing off the basic issue I'm having with one of the decorators from InversifyJS.
container.ts
import {fluentProvide} from "inversify-binding-decorators";
export const provideSingleton = (identifier: any) => {
return fluentProvide(identifier)
.inSingletonScope()
.done(true);
};
test.service.ts
import {provideSingleton} from './container'
#provideSingleton(TYPES.TEST)
export default class TestService {}
The strangest thing is when I put the provideSingleton in the same file as the TestService, everything works!?!?!
Basically to recreate the issue, simply follow the example from here: inversify-binding-decorators - Using #provideFluent multiple times. However there is an issue with the example, so please see this issue: fluentProvide example needed. The above provideSingleton reflects the changes from that issue. Then you simply import the provideSingleton function from another file instead of defining it in the same like in the example.
Can anyone explain to me what I'm missing? Why oh why would certain exported items not bee seen as the type they are? Is there a step I'm not seeing that NodeJS takes to make the item actually exported and therefore different? Can I force the function to resolve as a function so it can be used as such?
ENV:
NodeJS: 10.9.0
Typescript: 3.0.1
Mac: 10.13.16
So it looks like you can get issues like this when NodeJS can't handle a recursive import. I'm not exactly sure how to check you are getting this error other than your symptoms are like what I stated above. Basically the recursion caused my function to not load and therefore undefined is not a function.
It would be easy to notice if you had code like so:
a.ts
import B from './b';
export default class A extends B {}
b.ts
import A from './a';
export default class B extends A {}
In my case, I think my function provideSingleton did not like the file I put it in because of some conflicting code in the file, which all I had was:
import {Container} from 'inversify';
import "reflect-metadata";
import {fluentProvide} from "inversify-binding-decorators";
const container = new Container();
function ProvideSingleton(identifier: any) {
return fluentProvide(identifier)
.inSingletonScope()
.done(true);
}
export {container, ProvideSingleton}
In the end, if this issue comes up, try another file for your function and pay good attention to how the order of the loading happens. Although NodeJS handles recursive imports most of the time, you can still trip it out.

Having error "Module 'name' resolves to an untyped module at..." when writing custom TypeScript definition file

I can't find TypeScript definition #type/{name} for one of my installed NodeJS packages, so I attempt to write a d.ts file for it, and put the file in {project root}\typings folder. This is how I do:
// My source code: index.ts
import Helper from 'node-helper-lib';
// My definition: \typings\node-helper-lib.d.ts
declare....(something else)
declare module 'node-helper-lib' {
class Helper { ... }
export = Helper;
}
However, Visual Studio Code keeps yielding this error and puts red line under declare module 'node-helper-lib':
[ts] Invalid module name in augmentation. Module 'node-helper-lib'
resolves to an untyped module at '{project
path}\node_modules\node-helper-lib\index.js', which cannot be
augmented.
Isn't it legit that because the library is untyped, so I should be allowed to add typing to it?
UPDATE:
I am using:
TypeScript: 2.1.4
Visual Studio Code: 1.9.1
Node JS: 6.9.4
Windows 10 x64
The actual solution is given in a comment by #Paleo in #hirikarate's answer:
Imports should be declared inside the module declaration.
Example:
declare module 'node-helper-lib' {
import * as SomeThirdParty from 'node-helper-lib';
interface Helper {
new(opt: SomeThirdParty.Options): SomeThirdParty.Type
}
export = Helper;
}
After some tries and errors, I found that augmentation means "declaring a module in the same file with other module declaration(s)".
Therefore if we want to write a definition file for an untyped 3rd-party JavaScript library, we must have ONLY ONE declare module 'lib-name' in that file, and 'lib-name' must exactly match the library name (can be found in its package.json, "name" property).
On the other hand, if a 3rd-party library already has definition file .d.ts included, and we want to extend its functionalities, then we can put the additional definition in another file that we create. This is called augmenting.
For example:
// These module declarations are in same file, given that each of them already has their own definition file.
declare module 'events' {
// Extended functionality
}
declare module 'querystring' {
// Extended functionality
}
declare module '...' { ... }
I leave my discovery here just in case somebody has same question. And please correct me if I missed something.
The issue for me was that I was trying to declare the module in a .ts file. I changed it to .d.ts and it all worked just fine.
I was getting that error message too. The issue for me was that I was trying to declare another module in an existing type definition file that had a module declaration in it. After I moved the new module declaration to a new file, the error went away.

How to import node module in TypeScript without type definitions?

When I try to import node.js module in TypeScript like this:
import co = require('co');
import co from 'co';
without providing type definitions, both lines reports same error:
error TS2307: Cannot find module 'co'.
How to import it correctly?
The trick is to use purely JavaScript notation:
const co = require('co');
Your options are to either import it outside TypeScript's module system (by calling a module API like RequireJS or Node directly by hand) so that it doesn't try to validate it, or to add a type definition so that you can use the module system and have it validate correctly. You can stub the type definition though, so this can be very low effort.
Using Node (CommonJS) imports directly:
// Note there's no 'import' statement here.
var loadedModule: any = require('module-name');
// Now use your module however you'd like.
Using RequireJS directly:
define(["module-name"], function (loadedModule: any) {
// Use loadedModule however you'd like
});
Be aware that in either of these cases this may mix weirdly with using real normal TypeScript module imports in the same file (you can end up with two layers of module definition, especially on the RequireJS side, as TypeScript tries to manage modules you're also managing by hand). I'd recommend either using just this approach, or using real type definitions.
Stubbing type definitions:
Getting proper type definitions would be best, and if those are available or you have time to write them yourself you should definitely should.
If not though, you can just give your whole module the any type, and put your module into the module system without having to actually type it:
declare module 'module-name' {
export = <any> {};
}
This should allow you to import module-name and have TypeScript know what you're talking about. You'll still need to ensure that importing module-name does actually load it successfully at runtime with whatever module system you're using, or it will compile but then fail to actually run.
I got an error when I used the "Stubbing type definitions" approach in Tim Perry's answer: error TS2497: Module ''module-name'' resolves to a non-module entity and cannot be imported using this construct.
The solution was to rework the stub .d.ts file slightly:
declare module 'module-name' {
const x: any;
export = x;
}
And then you can import via:
import * as moduleName from 'module-name';
Creating your own stub file lowers the barrier to writing out real declarations as you need them.
Just import the module the following way:
import 'co';

Resources