如何正确键入更高阶函数

时间:2017-02-21 16:02:47

标签: 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 "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;
});

哪种方式按我想要的方式工作(我在setName上得到参数类型检查)但是我在log函数返回时遇到编译错误:

  

错误:TS2322:输入&#39;(... args:any [])=&gt;任何&#39;不能分配给&#39; F&#39;。

即使我使用<F extends (...args: any[]) => any>,我也会遇到同样的错误。基本上,我不知道如何使返回的函数满足泛型F

我该怎么做?

2 个答案:

答案 0 :(得分:2)

功能过载

可以使用

Function overloads。这是一种将定义与实现分开的方法:

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

我喜欢重载版本,因为当模块作为预编译的NPM包分发时,它有助于生成TypeScript定义。

使用类型断言as F

类型Function太宽。您的包装函数必须是以下的子类型:(...args: any[]) => any

您可以使用as F来说明TypeScript对返回的函数有信心:

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

使用类型断言as any

相同的实现,但我们可以使用Functionas any代替(...args: any[]) => anyas F(请参阅路易斯的回答)。

答案 1 :(得分:2)

您可以做出的最直接的修改是将返回值的类型强制为/** * Find the root {@code WebApplicationContext} for this web app, typically * loaded via {@link org.springframework.web.context.ContextLoaderListener}. * <p>Will rethrow an exception that happened on root context startup, * to differentiate between a failed context startup and no context at all. * @param sc ServletContext to find the web application context for * @return the root WebApplicationContext for this web app, or {@code null} if none * @see org.springframework.web.context.WebApplicationContext#ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE */ public static WebApplicationContext getWebApplicationContext(ServletContext sc) { return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE); }

any

这个编译文件,执行此操作后,您的function log<F extends Function>(fn: F): F { return ((...args: any[]) => { console.log("Calling", fn.name); return fn(...args); }) as any; } 函数将与传递给setName的函数具有相同的签名。