有没有一种方法可以正确地告诉流程,我返回的函数具有与传递的函数相同的签名,但功能不完全相同?
这是一个“一次”包装器的示例,该包装器可以防止多次调用一个函数,它可以工作,但是在内部使用一个任意广播来放弃流,我想摆脱这种类型,并具有100%的覆盖率:
module.exports.once = /*::<F:Function>*/(f /*:F*/) /*:F*/ => {
let guard = false;
return ((function () {
if (guard) { return; }
guard = true;
return f.apply(null, arguments);
}/*:any*/) /*:F*/);
};
好的,首先是第一件事。
当前,F
如果不进行强制转换,返回值将永远无法匹配,any
因为您要返回的函数的签名不相同,因为它可以返回undefined
原函数可能不返回的地方。
(删除注释语法以提高可读性)
module.exports.once = <F: Function>(f: F): F => {
let guard = false;
return ((function () { // this function returns the return value of F or void
if (guard) { return; } // returning void
guard = true;
return f.apply(null, arguments);
}: any): F);
};
但是,要开始键入此代码,我们将需要对该泛型函数进行一些分解。
首先,我们不要使用,Function
因为如果不这样做,通常会更好:
但是,如果您需要选择退出类型检查器,并且不想一路走下去,可以改用Function。功能不安全,应避免使用。
另外,我们将提取参数的类型和返回值,以便我们可以独立地操作它们并构造返回类型。我们会打电话给他们Args
,并Return
让他们很容易遵循。
module.exports.once = <Args, Return, F: (...Array<Args>) => Return>(
f: F
) ((...Array<Args>) => Return | void) => { // note `Return | void`
let guard = false;
return function () {
if (guard) { return; }
guard = true;
return f.apply(null, arguments);
};
};
现在我们考虑到我们的新函数可能会返回void
所有类型检查正常的结果。但是,当然,我们once
函数的返回类型将不再与传递函数的类型匹配。
type Func = (number) => string;
const func: Func = (n) => n.toString();
const onceFunc: Func = module.exports.once(func); // error!
// Cannot assign `module.exports.once(...)` to `onceFunc` because
// undefined [1] is incompatible with string [2] in the return value.
有道理吧?
因此,让我们讨论此功能的签名。我们希望我们的返回值具有与传递的函数相同的签名。当前不是,因为我们要添加void
到签名中。我们需要吗?我们为什么要返回undefined
?我们如何总是从我们的函数中返回相同的类型?好吧,一种选择是存储从单个调用到函数的返回值,并始终为后续调用返回存储的返回值。这是有道理的,因为重点是允许多次调用,但不执行任何功能效果。因此,通过这种方式,我们可以避免更改函数的接口,因此我们实际上不需要知道函数是否已被调用过。
module.exports.once = <Args, Return, F: (...Array<Args>) => Return>(
f: F
): ((...Array<Args>) => Return) => {
let guard = false;
let returnValue: Return;
return function () {
if (guard) { return returnValue; }
guard = true;
returnValue = f.apply(null, arguments);
return returnValue;
};
};
type Func = (number) => string;
const func: Func = (n) => n.toString();
const onceFunc: Func = module.exports.once2(func);
此时要问的一个好问题是:即使我们在技术上不完全返回,为什么类型仍然匹配F
?答案是因为流程中的函数是结构化的。因此,如果它们具有相同的参数和返回值,则它们的类型匹配。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句