Error: Cannot invoke an expression whose type lacks a call signature
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:
The error says
Cannot invoke an expression whose type lacks a call signature.
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);
- 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.
Justin
Updated on January 31, 2021Comments
-
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 over 7 yearsOk 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 over 7 yearsIf I do this and change
public toggleBody: boolean;
topublic toggleBody: any;
it works. -
jonrsharpe over 7 years@Justin why did you expect anything else? You claim
this.toggleBody
should returnboolean
, but that's not consistent with the return value ofsetProp
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 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 over 7 years@Justin: No; you need to use templates.
-
Noitidart over 5 yearsDoes making it
: void
also tell TS to ignore it? -
SLaks over 5 years@Noitidart: No; that means it returns nothing.
-
cham over 5 yearsThis 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 almost 5 yearsThis response would benefit from explaining the right way to do things, with an example.