【问题标题】:Signature of lambda return types in TypeScriptTypeScript 中 lambda 返回类型的签名
【发布时间】:2018-01-21 09:56:27
【问题描述】:

在下面的 TypeScript 中,两个函数是相同的,只是我试图在 demoTwo 中显式声明返回类型。返回类型是一个函数,它本身将一个函数作为输入。我的问题是为什么我必须给出由 whyThis 表示的参数名称,因为它永远不会被使用?如果没有该位置的内容,代码将无法编译。

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

要清楚我在这里问的是 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)
  }
}

如果我们并排设置 demoTwo 的声明,我们有:

function demoTwo(): (whyThis:() => string) => void { //TS
def demoTwo(): (() => String) => Unit = { //Scala

唯一的主要区别是 TS 在 whyThis 位置需要一些东西,而 Scala 不需要。为什么会这样?

【问题讨论】:

    标签: typescript lambda


    【解决方案1】:

    打字稿中的所有函数参数都需要有一个名称,无论您是否决定使用它们。您要添加的是调用签名,它在运行时不使用,但会在编写代码时帮助您。

    如果您查看为这两个示例生成的 javascript 文件,它们都会输出完全相同的代码:

    function demoOne() {
        return function (input) {
            var result = input();
            console.log("Foo:", result);
        };
    }
    function demoTwo() {
        return function (input) {
            var result = input();
            console.log("Bar:", result);
        };
    }
    

    编辑:所以您是正确的,因为该参数在运行时从不使用,但是这些参数确实在编译时显示(取决于您的编辑器)。如果您不添加调用签名,编辑器将从运行时参数中推断出一个,并在编译时显示这一点。

    您可以将调用签名视为与其他人交流您的代码如何使用以及参数代表什么的一种方式,其编写方式对于使用您的代码的人来说非常容易理解。

    以下是 Visual Studio 将鼠标悬停在您的函数上方时显示的内容,这些代码定义在调用这些函数时也会显示在 VSCode 和其他编辑器中:

    演示一:

    演示二:

    【讨论】:

    • 确实如此。但是我的问题与您的陈述“您是否决定使用它们”有关。在这种情况下,参数 whyThis 永远不能使用,所以这只是对 Typescript 规范的疏忽,还是我没有看到它有什么目的?
    • 我已经编辑了我的答案以尝试回答您的问题,如果主题仍然不清楚,请告诉我。
    • 谢谢。由于某种原因看不到图像。另外,我认为您不清楚我在问什么。已经添加了一个 Scala 版本的代码,希望能澄清一些事情。
    【解决方案2】:

    我认为混淆源于签名的定义方式。注意下面的 3 个签名

    (number) => Void
    (x : number) => Void
    (_ : number) => Void
    

    最后两个是带有 number 类型参数的 void 函数的签名。然而,第一个是一个函数,它接受一个 Any 类型的 argumnet,带有一个名称编号。所以该语言需要一个名称后跟一个类型,如果省略类型,则假定为 Any 类型。

    所以,

    (() => string) => void
    

    无效,因为 '() => string' 不是有效名称,编译器在试图解析它时会感到困惑。

    最短的形式将使用 '_ : type'。

    但我同意省略名称并仅使用类型或名称是可选的会更好。我不确定他们为什么做出这样的设计决定,也许是为了避免与现有的 JavaScript 箭头函数(无类型)混淆。

    【讨论】:

    • 这是一个很好的解释,但(正如你所说)他们为什么首先做出这个设计决定仍然是个谜。这就是我真正想要了解的......
    【解决方案3】:

    Function Type Expressions – Typescript Handbook 部分,他们有一个关于语法的注释,例如(a: string) => void:

    请注意,参数名称是必需的。函数类型(字符串) => void 的意思是“一个函数,其参数名为任何类型的字符串”!

    检查typescript.ebnf中的语法(非官方,取自SO answer),标识符名称确实是强制性的:

    // 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
    

    我认为可以将(() =&gt; string) =&gt; void 解析为带有FunctionType 参数的函数,以某种方式考虑匿名Identifier。不知道语法会变得多么棘手。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-16
      • 2021-10-17
      • 2014-02-23
      相关资源
      最近更新 更多