Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Constructor error (TS2351) in TypeScript when using ESM since v5 #81

Open
abouvier opened this issue Nov 1, 2024 · 7 comments
Open

Comments

@abouvier
Copy link

abouvier commented Nov 1, 2024

package.json

{
  "type": "module",
  "dependencies": {
    "fraction.js": "^5.1.0"
  },
  "devDependencies": {
    "typescript": "^5.6.3"
  }
}

tsconfig.json

{
  "compilerOptions": {
    "module": "node16"
  }
}

test.ts

import Fraction from "fraction.js";

let x = new Fraction(1.88);

Compilation error:

$ npx tsc
test.ts:3:13 - error TS2351: This expression is not constructable.
  Type 'typeof import(".../node_modules/fraction.js/fraction")' has no construct signatures.

3 let x = new Fraction(1.88);
              ~~~~~~~~


Found 1 error in test.ts:3

It worked fine in v4, and it works again if "type": "module" is added back to node_modules/fraction.js/package.json.

@infusion
Copy link
Collaborator

infusion commented Nov 1, 2024

What is your current nodejs version? For me it worked providing just the TS definition file. From v4 to v5 the environment setup was changed to work on all major platforms. The "type": "module" is something that makes a module a 100% ESM module, which breaks all older installations. Does it work with a named export?

import {Fraction} from "fraction.js";

@abouvier
Copy link
Author

abouvier commented Nov 1, 2024

What is your current nodejs version?

I tested with nodejs version 22.9.0 and 23.1.0.

Does it work with a named export?

Nope:

$ npx tsc
test.ts:1:9 - error TS2614: Module '"fraction.js"' has no exported member 'Fraction'. Did you mean to use 'import Fraction from "fraction.js"' instead?

1 import {Fraction} from "fraction.js";
          ~~~~~~~~


Found 1 error in test.ts:1

By removing default on the line export default class Fraction { in node_modules/fraction.js/fraction.d.ts then it works with import {Fraction} from "fraction.js";

@infusion
Copy link
Collaborator

infusion commented Nov 1, 2024

That's an interesting insight, thanks! I tested it again and again with nodejs from v16 to v23 and never had a problem. However, since your hint is very valuable, I updated the .d.ts file to support named and default export, same as the ESM export. Hope that also fixes your issue!

@abouvier
Copy link
Author

abouvier commented Nov 2, 2024

That's an interesting insight, thanks! I tested it again and again with nodejs from v16 to v23 and never had a problem. However, since your hint is very valuable, I updated the .d.ts file to support named and default export, same as the ESM export. Hope that also fixes your issue!

Yes now import {Fraction} from "fraction.js"; is working, but the default still not.

It works though if fraction.d.ts is renamed to fraction.d.mts (and the name updated in package.json).
As explained here, each file extension should reflect the type of module used in the file, because without "type": "module" in package.json then fraction.d.ts is considered a CommonJS file.

@abouvier
Copy link
Author

abouvier commented Apr 7, 2025

Are the types wrong?

Explanation of the problem: https://github.com/arethetypeswrong/arethetypeswrong.github.io/blob/main/docs/problems/FalseCJS.md

@infusion
Copy link
Collaborator

infusion commented Apr 7, 2025

Thanks for the test tool. I couldn't grasp the problem of the issue yet, since I'm not a TS developer, but still want to maintain the highest compatibility. Is it generally possible to give TS the correct exports for CJS and MJS? "type": "module" is not possible, since it would break the CommonJS import. The solution in the JS world is to maintain the exports field in the package.json. After a lot of trouble this now works perfectly fine, the question is how the TS compiler can get the right definition for each regime.

@abouvier
Copy link
Author

abouvier commented Apr 7, 2025

You probably need to maintain one type file for each engine, like in this library for example:

MikeMcl/bignumber.js@a543ab5
MikeMcl/bignumber.js#371

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants