Typescript 如何正确键入高阶函数

Typescript 如何正确键入高阶函数,typescript,Typescript,我有一个高阶函数,例如在vanilla JS中,如下所示: function log(fn) { return (...args) => { console.log("Calling", fn.name); return fn(...args); } } let name; const setName = log((newName) => { name = newName; }); setName("Hello"); // output "Callin

我有一个高阶函数,例如在vanilla JS中,如下所示:

function log(fn) {
  return (...args) => {
    console.log("Calling", fn.name);
    return fn(...args);
  }
}
let name;
const setName = log((newName) => {
   name = newName;
});
setName("Hello"); // output "Calling setName"
使用方法如下:

function log(fn) {
  return (...args) => {
    console.log("Calling", fn.name);
    return fn(...args);
  }
}
let name;
const setName = log((newName) => {
   name = newName;
});
setName("Hello"); // output "Calling setName"
如何在TS中正确地键入此项,以便
setName
具有正确的函数签名?意思是:

setName("hello") // OK
setName() // Error, missing arg
setName(123) // Error, number not string
到目前为止,我想到的是:

function log<F extends Function>(fn: F): F {
  return (...args: any[]) => {
    console.log("Calling", fn.name);
    return fn(...args);
  }
}

let name;
const setName = log((newName: string) => {
  name = newName;
});
功能日志(fn:F):F{
return(…args:any[])=>{
console.log(“调用”,fn.name);
返回fn(…args);
}
}
让名字;
const setName=log((newName:string)=>{
name=newName;
});
这是我想要的工作方式(我在
setName
上得到参数类型检查),但在
log
函数返回上得到编译错误:

错误:TS2322:类型“(…args:any[])=>any”不可分配给类型“F”

即使我使用
any>
我也会得到同样的错误。基本上,我不知道如何使返回的函数满足泛型类型
F

如何使用函数重载执行此操作?

可以使用。这是一种将定义与实施分开的方法:

function log<F extends Function>(fn: F): F
function log(fn: Function) {
    return (...args) => {
        console.log("Calling", fn.name);
        return fn(...args);
    }
}
与任何类型断言一样使用类型断言 相同的实现,但我们可以使用
函数
作为任意
,而不是
(…args:any[])=>任意
作为F
(参见Louis的答案)。

使用函数重载 可以使用。这是一种将定义与实施分开的方法:

function log<F extends Function>(fn: F): F
function log(fn: Function) {
    return (...args) => {
        console.log("Calling", fn.name);
        return fn(...args);
    }
}
与任何类型断言一样使用类型断言
相同的实现,但我们可以使用
函数
作为任意
,而不是
(…args:any[])=>任意
作为F
(参见Louis的答案)。

您可以做的最直接的修改是强制将返回值的类型设置为
任意

function log<F extends Function>(fn: F): F {
  return ((...args: any[]) => {
    console.log("Calling", fn.name);
    return fn(...args);
  }) as any;
}
功能日志(fn:F):F{
return((…args:any[])=>{
console.log(“调用”,fn.name);
返回fn(…args);
})如有的话;
}

这将编译文件,执行此操作时,
setName
函数将具有与传递到
log

的签名相同的签名。您可以进行的最直接的修改是强制将返回值的类型设置为
any

function log<F extends Function>(fn: F): F {
  return ((...args: any[]) => {
    console.log("Calling", fn.name);
    return fn(...args);
  }) as any;
}
功能日志(fn:F):F{
return((…args:any[])=>{
console.log(“调用”,fn.name);
返回fn(…args);
})如有的话;
}

这将编译文件,这样做,您的
setName
函数将具有与传递给
log
的签名相同的签名。谢谢,我没有想到使用这样的函数重载。我是根据其他参与者的想法进行编辑的。类型
函数
太宽。你的包装函数的类型必须是:
(…args:any[])=>any
。有趣的是,我用我的原始代码尝试了
F extends(…args:any[])=>any
,但由于某些原因,它仍然不允许我返回,即使带有那个确切的签名,
因为F
不能使用
F extends函数
。老实说,我不明白为什么会有不同,但它是有效的。我的想法是:
函数
是对象,它们可以有额外的成员。例如,函数接口:
接口setNameFn{(数据:string):void;其他成员:string;}
。您的包装器没有实现这种情况,我想为编译器提供一种方法来控制这种情况。但是,在一些测试之后,
log
似乎接受类型为
setNameFn
的函数。因此,更好的选择可能是最容易阅读的,使用
函数
作为任何
,而不是
(…args:any[])=>任何
作为F
。谢谢,我没有想到使用这样的函数重载。我是根据另一位撰稿人的想法编辑的。类型
函数
太宽。你的包装函数的类型必须是:
(…args:any[])=>any
。有趣的是,我用我的原始代码尝试了
F extends(…args:any[])=>any
,但由于某些原因,它仍然不允许我返回,即使带有那个确切的签名,
因为F
不能使用
F extends函数
。老实说,我不明白为什么会有不同,但它是有效的。我的想法是:
函数
是对象,它们可以有额外的成员。例如,函数接口:
接口setNameFn{(数据:string):void;其他成员:string;}
。您的包装器没有实现这种情况,我想为编译器提供一种方法来控制这种情况。但是,在一些测试之后,
log
似乎接受类型为
setNameFn
的函数。因此,更好的选择可能是最容易阅读的,使用
函数
作为任何
而不是
(…args:any[])=>任何
作为F
。谢谢,我发现这很有效,但我不喜欢使用
作为任何
。。。另外,为什么
as Function
不起作用呢?将
用作任何
并不比在另一个答案中使用
as F
更糟糕。不管怎样,你都在告诉编译器“相信我,我知道我在做什么”
as函数
无法工作,因为
F
由您传递的参数决定。到
日志
。如果
F
Function
的扩展,那么将
F
类型的对象分配给一个可以接受
Function
类型对象的变量是完全正确的,但是将
Function
类型的对象分配给一个可以接受
F
类型对象的变量是不正确的。如果将
用作函数,而不是将
用作任何
,那么您实际上是在尝试执行后者。我明白了,谢谢您的解释。基本上,这是因为您无法将基本类型分配给子类型。谢谢,我发现这是可行的,但我不喜欢像任何人那样使用
。。。另外,为什么
as Function
不起作用呢?将
用作任何
并不比在另一个答案中使用
as F
更糟糕。不管怎样,你都在告诉编译器“相信我,我知道我在做什么”<代码>作为功能
无法工作,因为
F
已确定