Reputation: 11264
In the TypeScript below both functions are the same, except that I'm trying to explicitly declare the return type in demoTwo. The return type is a function which itself takes a function as input. My question is why do I have to give the parameter name represented by whyThis, given that it will never be used? The code will not compile without something in that position.
function demoOne() {
return function(input: () => string) : void {
var result = input();
console.log("Foo:",result);
}
}
function demoTwo(): (whyThis:() => string) => void {
return function(input: () => string) : void {
var result = input();
console.log("Bar:",result);
}
}
var sampleInput = () => "wibble";
demoOne()(sampleInput);
demoTwo()(sampleInput);
To be clear what I'm asking here's the equivalent code in Scala:
object Program {
def demoTwo(): (() => String) => Unit = {
def tmp(input: () => String): Unit = {
val result = input()
println("Bar: " + result)
}
return tmp
}
def main(args: Array[String]): Unit = {
val sampleInput = () => "wibble"
demoTwo()(sampleInput)
}
}
If we set the declarations of demoTwo side by side we have:
function demoTwo(): (whyThis:() => string) => void { //TS
def demoTwo(): (() => String) => Unit = { //Scala
The only major difference is that TS requires something at the whyThis position and Scala does not. Why should this be the case?
Upvotes: 16
Views: 36755
Reputation: 131
In the section Function Type Expressions – Typescript Handbook they have a note about the syntax of, e.g. (a: string) => void
:
Note that the parameter name is required. The function type (string) => void means “a function with a parameter named string of type any“!
Checking the grammar in typescript.ebnf (not official, took from SO answer), the identifier name is indeed mandatory:
// A parameter list can basically be defined by
Parameter-List ::=
RequiredParameterList
| OptionalParameterList
| RestParameter
| // omitting combinations
// All parameter types require an Identifier, then an optional TypeAnnotation
<RequiredParameterList> ::= RequiredParameter (comma RequiredParameter)*
<OptionalParameterList> ::= OptionalParameter (comma OptionalParameter)*
RequiredParameter ::=
[AccessLevel] Identifier [TypeAnnotation]
| Identifier ':' ws-opt StringLiteral
OptionalParameter ::=
[AccessLevel] Identifier <'?'> [TypeAnnotation]
| [AccessLevel] Identifier [TypeAnnotation] Initialiser
RestParameter ::= "..." Identifier [TypeAnnotation] ws-opt
I think an argument could be made to parse (() => string) => void
as a function with a FunctionType
argument, somehow considering an anonymous Identifier
. Not sure how tricky the grammar would become.
Upvotes: 1
Reputation: 496
I think the confusion stems from the way the signatures are defined. Note the 3 signatures below
(number) => Void
(x : number) => Void
(_ : number) => Void
The last two are signatures of void functions that take an argument of type number. The first however is a function that takes an argumnet of type Any, with a name number. So the language requires a name followed by a type, and if the type is omitted then the Any type is assumed.
So,
(() => string) => void
Is not valid as '() => string' is not a valid name and the compiler is getting confused trying to parse it as such.
Shortest form will be using '_ : type'.
But I agree it would have been nicer for the name to be omitted and only type being used or the name being optional. I'm not sure why they made that design decision, perhaps to avoid confusion with existing JavaScript arrow functions (without types).
Upvotes: 14
Reputation: 1050
All function parameters in typescript need to have a name, no matter if you decide to use them or not. What you are adding is the call signature, something that is not used at runtime but will help you when writing code.
If you look at the generated javascript file for both of the examples, they will both output the exact same code:
function demoOne() {
return function (input) {
var result = input();
console.log("Foo:", result);
};
}
function demoTwo() {
return function (input) {
var result = input();
console.log("Bar:", result);
};
}
EDIT: So you are correct in that the parameter is never used during runtime, however these parameters do show during compile time (depending on your editor). If you do not add a call signature, the editor will infer one from the runtime parameters and show this during the compile time.
You can think of you call signature as a way to communicate to other people how your code is used and what the parameters represent, written to be very easy to understand for people that will consume your code.
Here is what Visual studio shows when hoovering above your functions, these code definitions also show up in VSCode and other editors when calling these functions:
Demo one:
Demo two:
Upvotes: 7