Can't import exported functions - node.js

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.

Related

Typescript imports destroys lookup?

I am trying to use a nodejs library like uuid in my typescript app. This simple import works just fine if I want to only use it in a specific class:
import { v4 } from "uuid";
class MyClass {
...
}
However, this makes the class MyClass not "discoverable" by any other files in my solution. Sure I could export it, but that forces me to import the class in every usage, and the problem spreads like a cancer to every file. Do I really have to import/export every single class/file in my application just because I want to produce a simple UUID?
I saw that I can use require instead, however typescript doesn't know what that keyword is. I found this question but neither installing #types/node nor the quick and dirty declare var require any works.
It really seems like I am jumping through a lot of unnecessary hoops just to generate a uuid in typescript. Am I doing something very wrong?
Thanks

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

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.

Is it possible to import a function in a modules subfolder

I'm trying to access the decode() method in the jsQR module.
I found an example that called decode() directly but that was from an HTML file, not nodejs.
In visual code I see this...
I know that the default export is defined in index.d.ts but is there anyway of importing the other classes/functions on the rest of the dist folder?
I've tried to import using require("jsqr/decoder") and require("jsqr/decoder/decode") to no avail.
EDIT
To be clear, I don't want jsQR, the default export. That deals with images. I'm trying to explicitly call the decode() method in the pic which accepts a BitMatrix
There are no limitations in what you can require from node_modules. So, with your case it should look like:
TypeScript\ESM Modules:
import { decode } from 'jsqr/dist/decoder/decoder';
CommonJS:
const { decode } = require('jsqr/dist/decoder/decoder');
UPD: If you take a look into dist folder for jsqr package, you can find that there are only d.ts files. Which means that you can not import it from there.
But, you can find an actual export of the module here:
Which means that you should able to import it from jsqr:
const { decode } = require('jsqr');

ts-node import not defined at runtime

I have the following reference import { STORE } from "../data/store"; It's part of a react project and works as expected.
However I need to run some code separately over STORE and accessed it the same way as it is in the react project, but ran via ts-node. When I try to access STORE by let data = STORE[videoId].labels;, with videoId set as "home" I get TypeError: Cannot read property 'home' of undefined.
Might anyone know what I'm missing --- must be something specific to ts-node...? Thanks!
In case someone arrives on this page because of imports issue, like I did, here's what my problem was:
I had three files : index.ts, File.json and File.ts
In index.ts:
import { thing } from "./File";
was working well in VS Code: I had auto-completion for thing and everything.
But when running ts-node index.ts, thing was undefined!
When I remembered that I had the json file, I just renamed File.ts to File.module.ts and changed the import to:
import { thing } from "./File.module";
and it was sorted.
It sounds like the property STORE is undefined..
If you change import { STORE } from "../data/store"; to import * as STORE from "../data/store" does it work?

Develop/Organize a node module in TypeScript

I want to develop a node module in TypeScript, but I'm having some problems with all the possible options to require, import, etc.
What I'm doing right now is having every class and interface in it's own file. So I would need to require all the dependencies which is kind of stupid, because I'm typing the class name twice, like this:
import Target = require('./Target');
export interface IFace {
getTarget(): Target.Target
}
I could write import t = require('./Target'); instead but then I need to write t.Target which I think is also pretty ugly.
And also I can't give it a module name (like FaceApp), because when I need to import two files, there's a naming conflict.
Obviously that would not be needed if everything would live in one file, but this is far from optimal I think.
So how do you guys organize your node module in TypeScript? I'd be happy to hear your suggestions.
You can avoid the name duplication by using the export = syntax. i.e. Do:
class Target{}
export = Target;
instead of export class Target.
Also grunt-ts transformers can help you with the import statement explosion : https://github.com/grunt-ts/grunt-ts/issues/85#issue-29515541
The way recommended by TypeScript is to do
export default class Target {}
and then you can do a true typescript import with
import Target from './Target'
alternatively, you can rename it
import NewName from './Target'
Also note that you can export multiple things from a file if they are related
export class SomeClass {}
export class OtherClass {}
And that on import, you can change the names
import { SomeClass as MySomeClass, OtherClass as MyOtherClass } from './Target'

Resources