如何键入带注释的“函数包装器”(返回与参数具有相同签名的函数的函数)

迦勒·詹姆斯·德利勒(Caleb James DeLisle)

有没有一种方法可以正确地告诉流程,我返回的函数具有与传递的函数相同的签名,但功能不完全相同?

这是一个“一次”包装器的示例,该包装器可以防止多次调用一个函数,它可以工作,但是在内部使用一个任意广播来放弃流,我想摆脱这种类型,并具有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*/);
};
莱尔·安德伍德(Lyle Underwood)

好的,首先是第一件事。

当前,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] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

处理具有相同签名但参数含义不同的函数

具有相同参数的函数不会返回相同的值

具有两个或多个返回参数的函数注释

具有单个参数模板的成员函数包装器?

如何转换具有相同签名的函数类型?

如何从具有相同签名的 main 调用基类函数?

返回带参数的函数

如何键入提示函数的可选返回参数?

从具有相同签名的函数指针转换为函数指针,但参数的附加限定条件除外

创建一个通用函数,返回具有相同签名的函数

如何在TypeScript的嵌套对象中键入具有函数参数类型的函数参数?

在Python中,如何定义函数包装器来验证具有特定名称的参数?

如何声明具有带类型参数的特征的函数的实现?

如何声明一个泛型函数,该函数返回一个具有相同参数但其参数返回类型不同的函数(反应钩子)

Java类具有2个具有相同函数签名但返回类型不同的方法

具有函数作为模板参数的C ++函数调用包装器

返回带有包装器的函数对象

如何编写与副本构造函数具有相同签名的C ++构造函数?

如何从具有相同签名的方法或扩展函数中调用顶级函数?

如何访问与全局函数具有相同功能签名的类中的全局函数?

包装函数,无论函数参数如何,都返回promise

键入元组以便函数链具有有效的参数和返回类型

类构造函数如何具有相同类的参数?

如何通过导出导出相同的函数但具有不同的参数

如何实现对相同函数但具有不同参数的线程?

如何让函数返回具有相同项目类型的两个迭代器之一?

具有相似签名的变体C函数的Doxygen注释

如何在python中用相同的包装器包装许多函数

带参数的函数返回函数