just icon indicating copy to clipboard operation
just copied to clipboard

Just packages don't work with TS when "esModuleInterop": false (default)

Open ilearnio opened this issue 3 years ago • 5 comments

I have the following tsconfig.json:

{
  "compilerOptions": {
    "module": "commonjs",
    "declaration": true,
    "removeComments": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "allowSyntheticDefaultImports": true,
    "target": "es2020",
    "sourceMap": true,
    "outDir": "./dist",
    "baseUrl": "./",
    "incremental": true,
    "paths": {
      "src/*": ["src/*"]
    }
  }
}

Please note that it has "allowSyntheticDefaultImports": true and no "esModuleInterop" set, which means that it equals to false by default.

So with this setup, we usually import most of our packages with * as something, like so import * as cookieParser from 'cookie-parser'.

However with Just packages, like "just-pick", we can't import it at all, because both ways don't work for us (i.e. with * as or without it).

Importing a package like so makes the imported package to be undefined (with no TS type-checking errors):

import pick from 'just-pick'

console.log(pick) // <-- undefined

Importing like so works on the runtime but the TS type-cheker would shows an error

import * as pick from 'just-pick'

pick(obj, []) // <-- This expression is not callable. Type 'typeof import(".../node_modules/just-pick/index")' has no call signatures ts(2349)

// Howeever this way it works
(pick as any)(obj, [])

So we had to add "esModuleInterop": true to our tsconfig.json to make it work, which isn't that bad option, just feel like it's something that should be reported. We have quite a big project with a lot of packages working well, but installing Just packages didn't work.

PS:

Node version: v14.18.3 NPM version: 7.24.2 Typescript version: ^4.2.3

ilearnio avatar Feb 16 '22 13:02 ilearnio

Hi @ilearnio, sorry for the delay in replying. I'm not a heavy TS user so I had to do quite a bit of reading up on allowSyntheticDefaultImports and esModuleInterop.

I'm still not sure I understand all the use cases, but I am a little surprised it's failing. Can you confirm you are using the *.mjs file from Just? Since that declares an explicit default, I did not think esModuleInterop would be required. I wonder if the issue is the export {objectPick as default} syntax.

angus-c avatar Mar 05 '22 22:03 angus-c

Possibly fixed by #429?

angus-c avatar Mar 19 '22 19:03 angus-c

I can confirm that this is still the case. Using just-union 3.1.1

jblyberg avatar Sep 01 '22 18:09 jblyberg

This issue also applies to ESModules with the following config:

"target": "ES2020",
"lib": ["ES2020"],
"module": "NodeNext",
"moduleResolution": "NodeNext",

I found a description of the problem and a possible solution in the TypeScript repository:

https://github.com/microsoft/TypeScript/issues/50067#issuecomment-1201663658

And in documentation:

https://devblogs.microsoft.com/typescript/announcing-typescript-4-7/#package-json-exports-imports-and-self-referencing

So, it works. I checked it by manually making changes to the package.

"exports": {
    ".": {
        "require": {
            "types": "./index.d.ts",
            "default": "./index.js"
        },
        "import": {
            "types": "./index.d.mts", // Copy-Paste from the 'index.d.ts' file
            "default": "./index.mjs"
        }
    },
    "./package.json": "./package.json"
},

mrmlnc avatar Oct 13 '22 06:10 mrmlnc

For default import to work without esModuleInterop option, .cjs files must have exports like:

const fn = () => {};

module.exports = fn;
module.exports.default = fn;

See an example of changing build process

klaseca avatar Apr 08 '23 16:04 klaseca