AurelicButter / AniList-Node

A lightweight Node.js wrapper for the AniList API
https://katsurin.com/docs/anilist-node/
MIT License
43 stars 18 forks source link

Typescript error: this expression is not constructable #63

Open AxelTerizaki opened 1 year ago

AxelTerizaki commented 1 year ago

Description

When using moduleResolution: node16 or nodenext in tsconfig.json, building with a call to the module causes the following error :

$ tsc
services/animeList.ts:12:21 - error TS2351: This expression is not constructable.
  Type 'typeof import("C:/xxx/karaokemugen-server/node_modules/anilist-node/lib/types/index")' has no construct signatures.

12 const anilist = new Anilist();
                       ~~~~~~~

Steps to Reproduce

  1. Create a new typescript project with moduleResolution: node16
  2. Try to spawn a new instance of Anilist in your code as described in the README.

Environment Node.js Version: 18.12.1 (tyepscript version 5.0.2) AniList-Node Version: 1.13.1

Thanks for looking into this! This would help a lot with a project I'm trying to keep up-to-date.

AxelTerizaki commented 1 year ago

I tried a few things to fix this from my own code like

import { default as Anilist } from 'anilist-node';

But it didn't work.

I had to edit the module itself to make it work. I'll offer a PR.

AxelTerizaki commented 1 year ago

I actually wanted to suggest a PR but I'm not sure if what I did was the right way to do it as it introduces a breaking change.

Basically, declare class Anilist was replaced with export class Anilist and I then imported with import { Anilist } from 'anilist-node'

For some reason, with node16 moduleResolution, the default export is not seen as constructable.

AurelicButter commented 1 year ago

Can you provide more information about this with your TSConfig and/or snippet of code with the AniList import? I have been unsuccessful trying to replicate the issue.

Using the moduleResolution key, I've gotten import AniList from "anilist-node" (as in the documentation), import { default as Anilist } from "anilist-node" (from your second comment), and import { Anilist } from "anilist-node" (from third comment) all to work with this code without changing the typings.

import Anilist from "anilist-node";

const anilist = new Anilist();

anilist.genres().then(data => { console.log(data); });

For additional reference, this is my TSConfig file that I used for it. I tried both es2017 and es2022 with no luck.

{
    "compilerOptions": {
        "outDir": "./build",
        "allowJs": true,
        "target": "es2017",
        "moduleResolution": "node16",
        "module": "commonjs",
        "esModuleInterop": true,
    },
    "include": ["./src/**/*"]
}
AxelTerizaki commented 1 year ago

Thanks for the quick reply!

Here's the tsconfig.json for my project :

{
    "compilerOptions": {
        "target": "ESNext",
        "module": "es2022",
        // Search under node_modules for non-relative imports.
        "moduleResolution": "node16",
        // Process & infer types from .js files.
        "allowJs": false,
        // Don't emit; allow esbuild to transform files.
        "noEmit": true,
        // Enable strictest settings like strictNullChecks & noImplicitAny.
        // true is better but requires a global refactoring.
        "strict": false,
        // Disallow features that require cross-file information for emit.
        "isolatedModules": false,
        // Import non-ES modules as default imports.
        "esModuleInterop": true,
        // Import constants from JSON (locales).
        "resolveJsonModule": true,
        "sourceMap": true,
        "inlineSources": true,
        "sourceRoot": "/",
        "outDir": "dist/",
        "pretty": true,
        "removeComments": true,
        "downlevelIteration": true,
        "noUnusedParameters": true,
        "noUnusedLocals": true
    },
    "include": [
        "src"
    ]
}
AxelTerizaki commented 1 year ago

Note : I also have type: module in my package.json. A friend looked into this and realized it might trigger the issue. However, not sure how to fix this on my side... I have other ES modules I use withotu any issues though.

AurelicButter commented 1 year ago

Note : I also have type: module in my package.json.

Yep, this is the issue. Added that one line in my package.json with my TSConfig and got the error.

From what I found, it's the combination of type: module in your package.json and moduleResolution: node16 in your TSConfig that is causing an issue. Both node16 and nodenext values for moduleResolution returned the error which leads me to suggest that it's some legacy JavaScript that doesn't want to play nice (some of this code is years old as we speak). However, I got it to function with moduleResolution: node so this might be worth a try on your side to see if that's a good solution.

AxelTerizaki commented 1 year ago

The problem is that I actually did specify node16 because I'm transitionning from the node moduleResolution thing. Some packages like file-type won't work with moduleResolution set to node. Try to add the latest version of file-type to your package.json for example :)

AurelicButter commented 1 year ago

Yes, I see it. I think I got a full grasp of what's needed now.

Basically, declare class Anilist was replaced with export class Anilist and I then imported with import { Anilist } from 'anilist-node'

Looping back to this, I think this is the fix that we need to solve it. Since you need moduleResolution: node16, there is nothing on your end that can help alleviate the issue for the time being (minus updating the typings file locally yourself).

If you want to make a PR for this, go for it. I don't view this as a breaking change since:

  1. Only the typing have changed
  2. CommonJS context can still use import AniList from "anilist-node" without issue.

Just following the Contributing guide and it'll be fixed in no time.

AurelicButter commented 1 year ago

Closing as #64 fixes this issue.

AxelTerizaki commented 1 year ago

Hello again.

Sadly, it actually doesn't work :)

While typescript isn't whining anymore, I should have built and tested it all, because as soon as the code is executed, node complains Anilist isn't constructable, which seems right when you think about it. With the PR I made typings and actual code are not in sync anymore.

I'm sorry about that.

I tried to twist how I called/imported anilist-node but it just would either make typescript complain or node complain on runtime. In the end I gave up and added a ts-expect-error line above new Anilist() and it compiled and worked.

I looked into this with a knowledgeable friend and he thinks that it's an authentic typescript bug that might get fixed later because there's no valid reason why it should believe that Anilist() is not constructable as there's a constructor method, it's a class and all...

Sorry about the PR again ^^ It might be interesting to reopen this until it gets fixed in Typescript as ESM is a rather new feature of typescript itself.

AurelicButter commented 1 year ago

While typescript isn't whining anymore, I should have built and tested it all, because as soon as the code is executed, node complains Anilist isn't constructable, which seems right when you think about it.

That is pretty weird. I didn't get that error when I compiled and ran it so I don't know what is happening. I'll reopen this in the meantime. 👍