David Yeiser
David Yeiser

Reputation: 1568

Typescript call signature error for exported async function

I have a Typescript file (colorTheme.ts) that looks like this:

export default (async (key) => {
  console.log(key)
  const themeImport = await import(`../build/theme/${key}/color.js`)
  return themeImport
})()

And then I reference this function from a separate Typescript file like so:

import colorTheme from '../colorTheme'

colorTheme('test').then(color => {
  // do stuff
})

However, I get an error:

TS2349: Cannot invoke an expression whose type lacks a call signature. Type 'Promise' has no compatible call signatures.

I've googled around and tried things like:

export default (async (key: string) => {
  console.log(key)
  const themeImport = await import(`../build/theme/${key}/color.js`)
  return themeImport
})()

But to no avail. Typescript isn't my forte, it's a preexisting environment that I'm trying to work in. From what I understand I need to somehow setup types for the Promise maybe? But I'm not sure how to do that.

Update: Added a bit more fuller code sample for what I'm trying to do.

Upvotes: 2

Views: 4877

Answers (1)

Christian Vincenzo Traina
Christian Vincenzo Traina

Reputation: 10384

Give a look at the two trailing parenthesis:

(async (x) => {
  console.log(x)
})() <--

You are executing the function as you declare it. This is a so-called IIFE: Immediately Invoked Function Expression.

Let's split the export adding a variable:

const result = (async (x) => {
  console.log(x)
})();

export default result;

What's the value of result? Well, the value of result is equal to the return value of the function. If it was a normal function, this was equal to a function immediately resolved as undefined. Since it is an async function and we aren't returning anything, this means that the returned value is a Promise of undefined.

So what you are exporting is an already resolved promise! But... what about the parameter x?

Well, the function accepts a parameter x, but you are actually passing nothing. Watch again the trailing parenthesis, there is nothing inside, so you will see undefined in the console if you execute the code.

If instead you passed an argument, for example a string, you saw that string:

(async (x) => {
  console.log(x) // It prints banana!
})('banana')

So here is the point where you have to pass the argument, then the function gets immediately invoked and the result is exported.

Let's rewrite colorTheme.ts in a simpler way:

1. Add a variable

const result = (async (x) => {
  console.log(x)
})();

export default result;

2. Return undefined (it's the same of not returning)

const result = (async (x) => {
  console.log(x)
  return undefined;
})();

export default result;

3. Use Promise in place of async

const result = (x => {
  console.log(x)
  return Promise.resolve(undefined);
})();

export default result;

4. Don't invoke the function immediately

const f = function (x) {
  console.log(x)
  return Promise.resolve(undefined);
}

const result = f(undefined);

export default result;

So, this is basically what you exported. Now it ups to you to fix it according to what you want to get!

Upvotes: 4

Related Questions