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

This expression is not callable. using with TypeScript #104

Closed
SettingDust opened this issue Jun 1, 2023 · 6 comments · May be fixed by #118
Closed

This expression is not callable. using with TypeScript #104

SettingDust opened this issue Jun 1, 2023 · 6 comments · May be fixed by #118

Comments

@SettingDust
Copy link
Contributor

Refer: davidjerleke/embla-carousel#481

TS2349: This expression is not callable.   Type 'typeof import("E:/@extractus/node_modules/.pnpm/[email protected]/node_modules/micro-memoize/index")' has no call signatures.
@asciant
Copy link

asciant commented Aug 22, 2023

Not sure if this relates, but I had the same error message and it was caused by how I was importing.

import * as memoize from 'micro-memoize'; <--- not callable
import memoize from 'micro-memoize'; <-- callable

@planttheidea
Copy link
Owner

I mean yes, this is an aspect of ESM imports. By nature, a wildcard import is an object namespace of all exports from that location. A plain object is never callable, so naturally that would be the case. Nothing specific to this package; this is just how JS works.

If you wanted to use the namespace import, then you would have to refer to the default export (what the second line refers to) explicitly:

import * as memoize from 'micro-memoize';
const foo = memoize.default((bar: string => bar);

There really isn't much benefit to using the namespace, though, as there are no other exports than the default.

@asciant
Copy link

asciant commented Aug 26, 2023

I believe it related to types, which is how I personally ended up down that road. I had a map the default import to a singleton variable to get around the require purge in Remix's hot reloads. However the variable would throw a type any warning, and importing the namespace resolved that initial typing issue, but then I came across the issue that the namespace was not callable.

To resolve the typing issue, I ended up adding this line.

import memoize from "micro-memoize";
type Memoizer = typeof memoize;

const memoizer: Memoizer ... (removed the rest as it illustrates the point) 

@planttheidea
Copy link
Owner

I'm actually even more confused now, and am curious what you would need to type in this way. Can you provide a more complete repro? A TS playground, tiny repo with code I can pull in, something that shows what the actual issue is.

@asciant
Copy link

asciant commented Aug 26, 2023

Example showing typing scenario that was described by me above The routes aren't working properly, I assume it's some stackblitz thing but I don't have time to debug.

The Remix Run dev server, purges the require cash on hot reload, which means micro-memoize would lose the cache on each hot reload (only an issue in development, not an issue in production). To ensure the cache is persisted while developing the app, we assign the memoize function to the global object via a singleton function.

Within the /app/utils folder, there are two files that illustrate why I'm typing it that way. These files correspond to a component route that demonstrates the lack of types:

  • /app/utils/memoize-types-not-working.server.ts is imported into /app/routes/not-working.tsx and this file shows that the types aren't coming along for the ride (particularly when passing options).
  • /app/utils/memoize-types-working.server.ts is imported into /app/routes/working.tsx and this file shows that the types are now available (particularly when passing options).

There is probably some fancy way of fixing this, but this worked so I moved on. When I checked the issues of this project, I noticed that I had not callable issue when debugging my issue and thought I would reply as importing the namespace is sometimes promoted as a fix for typing issues (I don't know why).

While redundant, if you wanted to see the actual pages.

You may need to run npm run dev in the Terminal, although as I said, running the application is not required.

@planttheidea
Copy link
Owner

This again looks like a gap in understanding, in this case how TS works with requires. By default, TS will widen anything returned from require to any, which is why the explicit typing you're doing "fixes" it. Again, not specific to the package, just how the language operates.

A common hack to work around this is to do a typing like this:

require('micro-memoize') as typeof import('micro-memoize')

Casting as the type of the import itself causes automatic inheritance of the package types without the dance you're doing locally.

That said, I'm going to close this issue because I'm not seeing any issues specific to the package. If you can provide a simple repro to showcase an issue with the package types, feel free to open another issue.

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

Successfully merging a pull request may close this issue.

3 participants