Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Error: Cannot invoke an expression whose type lacks a call signature

The function that it returns has a call signature, but you told Typescript to completely ignore that by adding : any in its signature.


"Cannot invoke an expression whose type lacks a call signature."

In your code :

class Post extends Component {
  public toggleBody: string;

  constructor() {
    this.toggleBody = this.setProp('showFullBody');
  }

  public showMore(): boolean {
    return this.toggleBody(true);
  }

  public showLess(): boolean {
    return this.toggleBody(false);
  }
}

You have public toggleBody: string;. You cannot call a string as a function. Hence errors on : this.toggleBody(true); and this.toggleBody(false);


Let's break this down:

  1. The error says

    Cannot invoke an expression whose type lacks a call signature.

  2. The code:

The problem is in this line public toggleBody: string; &

it's relation to these lines:

...
return this.toggleBody(true);
...
return this.toggleBody(false);
  1. The result:

Your saying toggleBody is a string but then your treating it like something that has a call signature (i.e. the structure of something that can be called: lambdas, proc, functions, methods, etc. In JS just function tho.). You need to change the declaration to be public toggleBody: (arg: boolean) => boolean;.

Extra Details:

"invoke" means your calling or applying a function.

"an expression" in Javascript is basically something that produces a value, so this.toggleBody() counts as an expression.

"type" is declared on this line public toggleBody: string

"lacks a call signature" this is because your trying to call something this.toggleBody() that doesn't have signature(i.e. the structure of something that can be called: lambdas, proc, functions, methods, etc.) that can be called. You said this.toggleBody is something that acts like a string.

In other words the error is saying

Cannot call an expression (this.toggleBody) because it's type (:string) lacks a call signature (bc it has a string signature.)


I think what you want is:

abstract class Component {
  public deps: any = {};
  public props: any = {};

  public makePropSetter<T>(prop: string): (val: T) => T {
    return function(val) {
      this.props[prop] = val
      return val
    }
  }
}

class Post extends Component {
  public toggleBody: (val: boolean) => boolean;

  constructor () {
    super()
    this.toggleBody = this.makePropSetter<boolean>('showFullBody')
  }

  showMore (): boolean {
    return this.toggleBody(true)
  }

  showLess (): boolean {
    return this.toggleBody(false)
  }
}

The important change is in setProp (i.e., makePropSetter in the new code). What you're really doing there is to say: this is a function, which provided with a property name, will return a function which allows you to change that property.

The <T> on makePropSetter allows you to lock that function in to a specific type. The <boolean> in the subclass's constructor is actually optional. Since you're assigning to toggleBody, and that already has the type fully specified, the TS compiler will be able to work it out on its own.

Then, in your subclass, you call that function, and the return type is now properly understood to be a function with a specific signature. Naturally, you'll need to have toggleBody respect that same signature.


It means you're trying to call something that isn't a function

const foo = 'string'
foo() // error