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.
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:
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 P
in the current namespace. However, it will not import function p()
from the above example, because it has no named property in p
;
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.