我有一个高阶函数,它根据作为参数传递的泛型函数正确推断其返回函数的类型,用一个简单的例子可能更容易理解:
function someFunction<T>(f: (s: string) => T) {
return function(s: string): T {
return f(s)
}
}
因此,如果我传递一个返回 astring
的函数,则返回的函数将正确推断类型:
const funcStr = someFunction((s: string) => s)
// ^ const funcStr: (s: string) => string
如果我传递一个number
返回 a 的函数,它将返回一个函数,该函数又返回一个数字:
const funcNum = someFunction((s: string) => Number(s))
// ^ const funcStr: (s: string) => number
现在我想为高阶函数添加一个默认值,但一直无法使其工作:
// Type '(s: string) => number' is not assignable to type '(s: string) => T'.
function someFunction<T>(f: (s: string) => T = (s: string) => Number(s)) {
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
return function(s: string): T {
return f(s)
}
}
如何将默认函数添加到高阶函数并在使用另一个函数时仍使其推断类型?
从技术上讲,编译器抱怨是正确的。您始终可以使用类型断言来抑制默认函数参数上的错误,并为其提供默认类型参数,T
以便编译器在无法T
从参数中推断出时知道该怎么做:
function someFunction<T = number>(
f: (s: string) => T = ((s: string) => Number(s)) as any
) {
return function (s: string): T {
return f(s)
}
}
这将完全如您所愿:
const fNum = someFunction()
console.log(fNum("3.14159").toFixed(2)); // 3.14
const fStr = someFunction((s: string) => s)
但是这种方法的问题是类型参数T
是由 的调用者指定的someFunction()
,而不是由实现指定的。所以没有什么能阻止恶意和/或困惑的调用者做这样的事情:
const fOops = someFunction<string>();
调用者已指定T
为 a string
,即使T
的默认值为number
,它也已被显式string
类型覆盖。所以编译器认为它fOops
是 type (s: string) => string
,但当然在运行时我们知道它不会是:
try {
fOops("oopsie").toUpperCase(); // no compiler error
} catch (e) {
console.log(e); // fOops(...).toUpperCase is not a function
}
这就是编译器抱怨的原因;这是说它不能确定默认值f
将是适合T
调用者指定的类型。
如果您不关心这种可能性,那么这种方法很好,并且需要对您的代码进行最少的更改。
如果你想阻止的可能性fOops
样的情况,你可以改变someFunction()
成为一个重载函数有两个呼叫签名:
// call signatures
function someFunction(): (s: string) => number;
function someFunction<T>(f: (s: string) => T): (s: string) => T;
如果不someFunction()
带参数调用,则调用的是第一个调用签名,并且返回类型必须是(s: string) => number
; 此调用签名上根本没有泛型类型参数。
否则,如果您使用回调参数调用它,那么您将调用第二个调用签名;有一个类型参数T
对应于该回调的返回类型。
然后实现可以与之前类似,但您可以放松类型以防止投诉(这里我们将仅用any
作返回类型):
function someFunction(f: (s: string) => any = (s: string) => Number(s)) {
return function (s: string) {
return f(s)
}
}
现在您打算支持的呼叫仍然有效:
const fNum = someFunction()
console.log(fNum("3.14159").toFixed(2)); // 3.14
const fStr = someFunction((s: string) => s)
但是不可能使用类型参数调用零参数调用签名,因此您不能再以错误的方式调用它:
const fOops = someFunction<string>() // error!
// Expected 1 arguments, but got 0.
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句