在运行时确定回调函数的签名

时间:2018-02-21 13:21:00

标签: typescript

我想将不同的回调函数作为参数传递,并使用适当的参数调用它们。

这是一个如何运作的高度简化的例子。除了process instanceof ITwo没有任何意义,我找不到任何能够完成工作的表达。

    interface IOne { (j: string): number; }
    interface ITwo { (j: number): number; }
    function dualArg(i: string, process: IOne | ITwo): number {
        // something like this
        if (process instanceof ITwo) {
            // call with numeric arg
            return process(i);
        } else {
            // conver to number
            return process(+i);
        }
    }
    function inc(i: number): number {
        return ++i;
    }
    function idem(text: string): number {
        return +text;
    }
    it('determine function signature', () => {
        expect(dualArg('1', inc)).toBe(2);
        expect(dualArg('1', idem)).toBe(1);
    })

对于正常的参数instanceof足以让TypeScript将其视为特定类型的对象,但是对于函数似乎没有任何相似之处。

如果我使用某种硬编码条件,例如process.prototype.constructor.name === 'idem'我会收到Typescript错误消息:Cannot invoke an expression whose type lacks a call signature. Type 'IOne | ITwo' has no compatible call signatures.

当然,我可以定义process: any来禁用任何TypeScript检查,代码将编译并运行,但我的目标是能够仅通过其签名来区分这些功能(而不是依赖于其他约定)如名称或其他标志)。

2 个答案:

答案 0 :(得分:1)

问题是在运行时所有类型信息都会丢失。因此,您不能在运行时直接推断函数的类型(超出它是函数的事实)。

您可以做的是创建一个也具有确定类型的属性的函数类型。并使用函数来建立函数:

enum Type { One, Two}
interface IOne { (j: string): number; type: Type.One }
interface ITwo { (j: number): number; type: Type.Two}
function dualArg(i: string, process: IOne | ITwo): number {
    if (process.type === Type.One) {
        // Type guard above, process is of type IOne
        return process(i);
    } else {
        // Type guard above, process is of type ITwo
        return process(+i);
    }
}
function inc(i: number): number {
    return ++i;
}
function idem(text: string): number {
    return +text;
}

function oneFunction(fn: (j: string)=> number) : IOne{
    return Object.assign(fn, { type: Type.One as Type.One });
}

function twoFunction(fn: (j: number)=> number) : ITwo{
    return Object.assign(fn, { type: Type.Two as Type.Two });
}

dualArg("", twoFunction(inc));
dualArg("", oneFunction(idem));

对于您的简单示例,这将是过度的(您可以定义两个版本的dualArg)但是如果函数的创建和用法相距很远并且有更多的代码在两者之间重用方法可能有意义。

答案 1 :(得分:0)

TypeScript只是开发时间。你不能指望它在运行时做某事。 TypeScript能够使用您的运行时假设(如instanceof)“理解”事物。

看起来你的两个函数应该有一个带有两个重载的实现,而不是在dualArg(调用)函数中处理它。处理dualArg中的参数类型意味着您必须在想要调用这些函数的任何地方执行相同的操作。

那么你如何实现一个包装器函数来进行参数测试(在运行时),TypeScript会检测到它并保护你。