planttheidea/micro-memoize

`This expression is not callable.` using with TypeScript

Closed this issue · 6 comments

Refer: davidjerleke/embla-carousel#481

TS2349: This expression is not callable.   Type 'typeof import("E:/@extractus/node_modules/.pnpm/micro-memoize@4.1.2/node_modules/micro-memoize/index")' has no call signatures.

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

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.

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) 

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.

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.

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.