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

231,033

Solution 1

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

Solution 2

"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);

Solution 3

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.)

Solution 4

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

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

Solution 5

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.

Share:
231,033
Justin
Author by

Justin

Updated on January 31, 2021

Comments

  • Justin
    Justin over 3 years

    I am brand new to typescript, and I have two classes. In the parent class I have:

    abstract class Component {
      public deps: any = {};
      public props: any = {};
    
      public setProp(prop: string): any {
        return <T>(val: T): T => {
          this.props[prop] = val;
          return val;
        };
      }
    }
    

    In the child class I have:

    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);
      }
    }
    

    Both showMore and ShowLess give me the error, "Cannot invoke an expression whose type lacks a call signature."

    But the function that setProp returns DOES have a call signature, I think? I think I'm misunderstanding something important about typings of functions, but I don't know what it is.

    Thanks!

  • Justin
    Justin over 7 years
    Ok progress, Thanks! Now I get "error TS2322: Type '<T>(val: T) => T' is not assignable to type 'boolean'." If I remove the :any. I think this is why I added :any in the first place. I actually still get the original errors as well.
  • Justin
    Justin over 7 years
    If I do this and change public toggleBody: boolean; to public toggleBody: any; it works.
  • jonrsharpe
    jonrsharpe over 7 years
    @Justin why did you expect anything else? You claim this.toggleBody should return boolean, but that's not consistent with the return value of setProp that you've assigned to it. You seem to be just randomly throwing in types without thinking about what you actually want to send and return.
  • Justin
    Justin over 7 years
    @jonrsharpe Ok yes that does make sense. In this case it returns a boolean, but in general it returns any. So I have to use any?
  • SLaks
    SLaks over 7 years
    @Justin: No; you need to use templates.
  • Noitidart
    Noitidart over 5 years
    Does making it : void also tell TS to ignore it?
  • SLaks
    SLaks over 5 years
    @Noitidart: No; that means it returns nothing.
  • cham
    cham over 5 years
    This is one of the best answers, ever! I know all of those definitions but when I saw the warning message all those terms, in one dense sentence, were too much for my cluttered brain.
  • Andre M
    Andre M almost 5 years
    This response would benefit from explaining the right way to do things, with an example.