Definitelytyped: PouchDB typings are unusable

Created on 12 Sep 2017  路  5Comments  路  Source: DefinitelyTyped/DefinitelyTyped

  • [x] I tried using the @types/pouchdb package and had problems.
  • [x] I tried using the latest stable version of tsc. https://www.npmjs.com/package/typescript
  • [x] I have a question that is inappropriate for StackOverflow. (Please ask any appropriate questions there).
  • [x] [Mention](https://github.com/blog/821-mention-somebody-they-re-notified) the authors (see Definitions by: in index.d.ts) so they can respond.

    • Authors: @AGBrown @geppy @fredgalvao

It's impossible to use PouchDB typings as-is. When trying to import PouchDB from 'pouchdb', PouchDB is undefined with allowSyntheticDefaultImports, import * as PouchDB from 'pouchdb' won't compile, having to resort to var PouchDB = require('pouchdb').

Most helpful comment

@G-Rath: Thanks for pointing out that diff, that lends me some perspective on your particular problem. A general problem right now is that PouchDB itself has two different module shapes depending on how (what environment) you import it. Both answers are correct, but that also means that neither answer is truly correct because Typescript doesn't support a good way to support multiple different environments in the same typings.

The default export module shape based on the main field in package.json for PouchDB is still a CommonJS export = PouchDB (and according to the DefinitelyTyped README, the correct answer today, so you may be onto something asking for the reversion back to export = in the places you find them changed, but that will cause it's own havoc for reasons below).

If your environment supports loading jsnext:main or otherwise synthesizes a default you get an ESM export default PouchDB, and this is what you see currently in the typings in the mentioned diff.

(Worse, this isn't consistent across the family of PouchDB typings since that diff touched the main @types/pouchdb, but not all of the satellite typings, for instance @types/pouchdb-browser, which means that both export = PouchDB and export default PouchDB can current be found here in DT.)

PouchDB furthermore doesn't currently do any work to try to bridge both module shapes and guessing the wrong module shape blows up someone's code.

This dual module shape is especially complicated because of the difference between CommonJS modules and ES Modules. ES Modules have stricter shape requirements and the "module" object itself that you get when you import PouchDB = require('pouchdb') with the current typings (rather than import PouchDB from 'pouchdb') is an ES Module object, An ES Module is not callable or newable (as you see with the error about new that you receive) because it has some additional metadata and is a stronger proxy for its contents than a CommonJS exports object [^1], that's why they have a "special" field named default for exporting callable and/or newable things. You could try import PouchDB = require('pouchdb').default if you want to try the worst of both worlds today, and that might work or might blow up at runtime depending on whether you land in a runtime situation with jsnext:main or a synthetic default, which you might not.

Long story short:

  • You are correct that export default PouchDB should be reverted to export = PouchDB and I'll make a PR for that
  • PouchDB could do more to make this a more tolerable situation for everyone
  • Typescript is trying to help, but can't read the mind of you or your environment and we're in a very ugly part of the transition right now

[^1] Though technically the CommonJS spec itself never allowed exports = thing for very similar reasons to why ES Modules don't support anything like that, but NodeJS never followed the spec here, and well, here we are.

All 5 comments

+1 I am having the same exact issue... any help pls?

What module loader are using? Is it possible to convince your module loader to give you a synthetic default? Or use a dumb shim outside of your TS build to do that? (ie, a JS wrapper outside of your TS build that simply imports and then export default PouchDB)

We're at an unfortunate point in the CommonJS/ESM transition where it is getting increasingly tough to do the right thing for every user, and Typescript getting stricter about module shapes could be helpful, in edge cases like this it isn't helping as much as we'd all like. A joke I saw earlier today in another thread was that one day we'll all look back at this mess that CommonJS created as "the Dark Ages" of JS module systems.

{
  "compilerOptions": {
    "module": "commonjs",
    "target": "es2017",
    "allowSyntheticDefaultImports": true,
    "noEmitOnError": true,
    "listEmittedFiles": true,
    "inlineSourceMap": true,
    "noImplicitAny": true,
    "strict": true,
    "alwaysStrict": true,
    "noFallthroughCasesInSwitch": true,
    "moduleResolution": "node",
    "strictNullChecks": true,
    "lib": [
      "dom",
      "esnext.asynciterable",
      "es2015"
    ]
  },
  "files": [
    "index.ts"
  ]
}

@pocesar

What error are you getting?

When using import PouchDB = require('pouchdb'); I'm getting

TS2351: Cannot use 'new' with an expression whose type lacks a call or construct signature.

which I've tracked down to be because of this commit

https://github.com/DefinitelyTyped/DefinitelyTyped/commit/b629f5171e9fa2ab27a586f5d37de9c01edde92a#diff-94ddd632741875069027083e2383de71R27

which was about a month ago.

I reverted that line back, and now almost everything is working fine - The only thing that's not is WebStorm is having issues resolving the documentation (still does parameter hinting and suggestions, just it's internal documentation throws a hissy.

webstorm-example

I've made an issue about it on PouchDB's github project.

This fix was based on this project's PR, which has mention of the typescript documentation on modules.

@G-Rath: Thanks for pointing out that diff, that lends me some perspective on your particular problem. A general problem right now is that PouchDB itself has two different module shapes depending on how (what environment) you import it. Both answers are correct, but that also means that neither answer is truly correct because Typescript doesn't support a good way to support multiple different environments in the same typings.

The default export module shape based on the main field in package.json for PouchDB is still a CommonJS export = PouchDB (and according to the DefinitelyTyped README, the correct answer today, so you may be onto something asking for the reversion back to export = in the places you find them changed, but that will cause it's own havoc for reasons below).

If your environment supports loading jsnext:main or otherwise synthesizes a default you get an ESM export default PouchDB, and this is what you see currently in the typings in the mentioned diff.

(Worse, this isn't consistent across the family of PouchDB typings since that diff touched the main @types/pouchdb, but not all of the satellite typings, for instance @types/pouchdb-browser, which means that both export = PouchDB and export default PouchDB can current be found here in DT.)

PouchDB furthermore doesn't currently do any work to try to bridge both module shapes and guessing the wrong module shape blows up someone's code.

This dual module shape is especially complicated because of the difference between CommonJS modules and ES Modules. ES Modules have stricter shape requirements and the "module" object itself that you get when you import PouchDB = require('pouchdb') with the current typings (rather than import PouchDB from 'pouchdb') is an ES Module object, An ES Module is not callable or newable (as you see with the error about new that you receive) because it has some additional metadata and is a stronger proxy for its contents than a CommonJS exports object [^1], that's why they have a "special" field named default for exporting callable and/or newable things. You could try import PouchDB = require('pouchdb').default if you want to try the worst of both worlds today, and that might work or might blow up at runtime depending on whether you land in a runtime situation with jsnext:main or a synthetic default, which you might not.

Long story short:

  • You are correct that export default PouchDB should be reverted to export = PouchDB and I'll make a PR for that
  • PouchDB could do more to make this a more tolerable situation for everyone
  • Typescript is trying to help, but can't read the mind of you or your environment and we're in a very ugly part of the transition right now

[^1] Though technically the CommonJS spec itself never allowed exports = thing for very similar reasons to why ES Modules don't support anything like that, but NodeJS never followed the spec here, and well, here we are.

Was this page helpful?
0 / 5 - 0 ratings