【问题标题】:Is there a way in typescript to ensure a function's input signature is same as output one?打字稿中有没有办法确保函数的输入签名与输出签名相同?
【发布时间】:2020-08-18 04:16:45
【问题描述】:

一个函数需要另一个通用函数作为输入,它被定义为:

function higherOrderFunction(fn: Function): Function {
  // do something
  return (...args: any[]) => fn(...args);
} 

称为

function fun(x: number, y: string) { return `${x}${+y}`; }

const higherOrderFunctionWithFunction= higherOrderFunction(fun);
console.log(higherOrderFunctionWithFunction(1, '3'))

无论是 x 还是 y 类型,这都适用于 typescript(无编译错误)。

如何强制higherOrderFunction返回的函数只有x:number, y:string才能被TSC正确编译? (理想情况下适用于任意数量的参数和类型,而不仅仅是这种特定情况)

【问题讨论】:

    标签: typescript


    【解决方案1】:

    原来this PR 实现了目标。

    higherOrderFunction 需要重构为:

    function higherOrderFunction<A extends any[], B>(fn: (...a: A) => B) {
      return (...args: A) => fn(...args);
    }
    

    现在如果我们打电话给例子

    console.log(higherOrderFunctionWithFunction(1, 3))
    

    会扔

    “3”类型的参数不能分配给“字符串”类型的参数

    有效地强制低阶函数(fun)的签名(传递到higherOrderFunction并从higherOrderFunction返回)完全相同

    如果fun 接收超过2 个参数(其类型在fun 中指定),这也有效

    【讨论】:

      【解决方案2】:

      也许我没有关注,但如果高阶函数返回一个带有任何参数的函数,那么在调用时将返回传入函数的结果,通过参数传递 - 你不能只返回传入的函数吗?然后通用类型非常简单,我看不出你还需要什么类型的类型。

      function higherOrderFunction<T>(fn: T): T {
        // do something
        return fn;
      } 
      
      function fun(x: number, y: string) { return `${x}${+y}`; }
      
      const higherOrderFunctionWithFunction = higherOrderFunction(fun);
      console.log(higherOrderFunctionWithFunction(1, 3)) // error can't assign number to string
      

      编辑:

      如果您的高阶函数如下,那将会改变事情,我会改变我的答案。但是你有 // 在 return 之前做一些事情所以离开你的例子看起来你也可以只 return fn...

      function higherOrderFunction<T>(fn: T): T {
        return (...args) => {
            // do something common on every call
            return fn(...args);
        };
      } 
      

      编辑2: 对于上述情况,如果您真的需要将 fn 包装在另一个函数中 - 这是我能够获得您正在寻找的类型检查的唯一方法,没有其他错误。我必须使用类型断言来返回您的高阶函数来断言 T 正在被返回并且它没有被实例化为不同的子类型......不能说我在这个特定情况下 100% 理解那里的问题但是与此有关:could be instantiated with a different subtype of constraint 'object'

      function higherOrderFunction<T extends (...args: any[]) => unknown>(fn: T): T  {
        return ((...args) => {
            // do something common on every call
            return fn(...args);
        }) as T;
      }
      
      function fun(x: number, y: string) { return `${x}${+y}`; }
      
      const higherOrderFunctionWithFunction = higherOrderFunction(fun);
      console.log(higherOrderFunctionWithFunction(1, 3)) // error can't assign number to string
      

      编辑3: 看着你的回答,我更喜欢它并且赞成,我想你明白了......

      【讨论】:

      • type '(...args: any[]) => any' 不可分配给类型 'T'。 'T' 可以用与 '(...args: any[]) => any'.ts(2322) Rest parameter 'args' 隐含的 'any[]' 类型无关的任意类型来实例化。使用(...args:any[]) 也不会这样做
      • 请问?你指的是什么?第一个 sn-p 我展示了一个根本不使用 ...args 的解决方案 - 然后我用一个伪代码示例询问了一个关于需求的问题,如果你尝试实际使用它,yes 会出现类型错误,它只是一个你的 //do something 代码实际上要去哪里的例子。你能确认你将在哪里“做某事”吗?高阶块还是在你返回的函数中?
      • 您的 OP 将它放在高阶块中 - 如果这实际上是您计划//做某事的地方 - 那么您不需要围绕 fn 的包装函数。您需要将 fn 包装在另一个函数中的唯一原因是,如果您想在调用 fn 时运行一些通用代码,这不是您的 OP 读取的方式。
      • 我应该澄清一下。高阶函数确实应该返回一个函数 (... args) =>(common code)。重要的是 fn 传递给高阶函数可以强制执行 fn 参数(在我的问题中:x 和 y 分别作为数字和字符串,但理想情况下 fn 可以具有任意数量/类型的参数)
      • 嗯,这并没有说明为什么你需要返回另一个函数,这就是我所指出的 - 你的 OP 读取的方式是你返回的函数实际上什么都不做,只是通过参数到 fn 并返回结果。基于此,您不需要包装功能。如果您在该函数中做其他事情,那么您应该在您的 OP 中表明我要说的就是让问题更清楚。
      猜你喜欢
      • 1970-01-01
      • 2010-09-13
      • 2020-05-28
      • 1970-01-01
      • 2022-12-05
      • 1970-01-01
      • 1970-01-01
      • 2018-12-15
      • 1970-01-01
      相关资源
      最近更新 更多