-1

Adapting the recommended typescript example in my program

import * as pgPromise from 'pg-promise' ;
const pg = pgPromise() ;

did not work. The second line caused error:

This expression is not callable. Type 'typeof pgPromise' has no call signatures.ts(2349)

Googling the error message did not return answers that would seem relevant to module import as they all deal with calling a non-callable object. However, I need a different kind of explanation: I don't know why the import statement produces a non-function object, although the obvious authors's intention is to return a function that would enable initialization of the underlying module with a number of parameters.

Analyzing module source code did not help much, it is written in javascript and does not look very transparent.

  • [Here](https://github.com/vitaly-t/pg-promise/tree/master/typescript) is a simple example but it looks similar to your code. [Here](https://github.com/vitaly-t/pg-promise-demo) you can find a TypeScript demo. –  Apr 04 '21 at 12:54
  • And that is exactly the one that does NOT work, as I described in my question - it returns the error mentioned above. Have you ever tried to run it in real environment? – Jindrich Vavruska Apr 04 '21 at 14:51
  • Yes, I downloaded the demo, traversed into folder `TypeScript`, installed `@types/express` with `npm i @types/express` and transpiled with the latest TypeScript version. It works. No error. No warning. –  Apr 04 '21 at 14:57
  • Express??? I am talking about pg-promise. I don't need express for anything. – Jindrich Vavruska Apr 04 '21 at 18:39
  • [The way it is shown in the project](https://github.com/vitaly-t/pg-promise/tree/master/typescript) should be sufficient. There must be an issue on your side, if it doesn't work. The library doesn't go in length about TypeScript specifically, because it is the same as for JavaScript. – vitaly-t Apr 04 '21 at 20:33
  • You asked me if I've ever tried to run it. I described you the exact steps I did to transpile the project. The demo project uses express. To remove the transpiler errors I had to install `@types/express`. Did you even read the documentation and looked at the demo project? –  Apr 05 '21 at 11:04
  • I did google the error message and id not find anything useful. The module code is quite complex, may I say obfuscated, so looking for dependencies is a nightmare. However, I was able to find the correct syntax, which is, in addition, less complicated than the original. Apparently, the module author exports namespace and in this case `import * as ...` syntax is not possible as default (probably needs TS configuration tweaking, but since my current TS works with all sorts of other projects, why change it because of just one database driver...). – Jindrich Vavruska Apr 12 '21 at 08:12

2 Answers2

1

This is an issue related to transpilation to javascript and ES6 specification, not specific to pg-promise.

There are at least two possible ways how to solve this issue:

  1. Use different syntax:
import pgPromise from 'pg-promise' ;

This line imports the default export object (in this case function) as-is. It is shorter, and, in my opinion more intuitive than the following line, used in the example and recommended by module author:

import * as pgPromise from 'pg-promise' ;

requires, that the imported object is an Object (because * means importing all of its properties to the main namespace) and therefore it does import all exported properties, but not the function, because the function is not assigned to a property, it is the object itself.

To explain the difference in more detail,

This is a "plain" object:

// module "module-p.js"
let p = { name: 'John', colour: 'blue', age: 1263 }
export default p ;

and this is a function object with properties:

let p = (x) => { console.log(x); } ;
p.name = 'John';
p.colour = 'blue';
p.age = 1263;
export default p ;

The latter, when imported by

import * as P from 'module-p';

will import object properties name, colour and age and assign them to object Pin the current namespace. However, it will not import function p() from the above example, because it has no named property in p;

  1. Quick and dirty solution is to fix option esModuleInterop in tsconfig.json:
"esModuleInterop": true

to

"esModuleInterop": false

Changing the option will enable the relaxed module import handling needed for the javascript module pg-promise and other modules written in similar javascript style.

https://www.typescriptlang.org/tsconfig#esModuleInterop says:

By default (with esModuleInterop false or not set) TypeScript treats CommonJS/AMD/UMD modules similar to ES6 modules. In doing this, there are two parts in particular which turned out to be flawed assumptions:

  • a namespace import like import * as moment from "moment" acts the same as const moment = require("moment")

  • a default import like import moment from "moment" acts the same as const moment = require("moment").default

This mis-match causes these two issues:

  • the ES6 modules spec states that a namespace import (import * as x) can only be an object, by having TypeScript treating it the same as = require("x") then TypeScript allowed for the import to be treated as a function and be callable. This breaks the spec’s recommendations.

  • while accurate to the ES6 modules spec, most libraries with CommonJS/AMD/UMD modules didn’t conform as strictly as TypeScript’s implementation.

The reason why pg-promise import depends on particular Typescript transpiler configuration appears to be that the exported namespace is a function, not an object, which is not allowed when esModuleInterop = true. pg-promise documentation does not mention any specific typescript configuration dependencies or requirements.

Sadly, the author of pg-promise decided to leave arrogant comments here instead of pointing the typescript issue in an answer.

  • There was a warning message that hinted to solution #1: src/read-adif.ts:3:1 `import * as pgPromise from 'pg-promise'` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ `Type originates at this import. A namespace-style import cannot be called or constructed, and will cause a failure at runtime. Consider using a default import or import require here instead.` – Jindrich Vavruska Apr 14 '21 at 07:25
0

Finally I found the answer myself.

There is a bug in the example. The original import declaration:

import * as pgPromise from 'pg-promise' ;

results in error, as described in the question. The correct syntax is:

import pgPromise from 'pg-promise' ;

and then it works as expected.

  • There is no bug. That's your TypeScript configuration issue. Your project is set to use the default import, with `esModuleInterop`. That's why it forces default import. This is TypeScript basics, it is not a problem with `pg-promise`. – vitaly-t Apr 12 '21 at 10:19
  • 1
    Vitaly, I think you should fix your self-description: `I do a lot of support on StackOverflow, helping developers with the libraries that I developed on GitHub, and some new technologies.` I would suggest to modify it to `I present myself on StackOverflow a lot, explaining developers that libraries I developed are perfect and flawless and tell them to learn hard and solve there problems themselves`. Which of the two describes better your exhibition here, what do you think? – Jindrich Vavruska Apr 14 '21 at 07:31
  • BTW, there is a saying "there are no wrong questions, just wrong answers". Also something to think about... – Jindrich Vavruska Apr 15 '21 at 05:43