繁体   English   中英

如何根据联合类型的接口属性确定函数的参数行为?

[英]How to determine argument behavior of a function based on an interface property that is a union type?

我有一个接口,它接受一个函数,该函数可以是函数类型或字符串(对于异步函数,因此 tsc 不会转换它们)。 问题出在我的任务运行器上,我似乎无法弄清楚如何根据接口上的func属性确定函数的参数行为。 它将它推断为string | (...args: any[]) => number) string | (...args: any[]) => number)例如在func: (x: number) => x + 1) 问题是当我尝试检查run函数调用中的 func 参数以使其需要带参数的函数的参数时,它没有正确检查它是否扩展了函数,而是将参数推断为any[]而不是number . 有谁知道如何做这样的事情?

我在这里的意思的例子:

interface ITask<T> {
    id: number;
    func: ((...args: any[]) => T) | string;
};

class Task<T> implements ITask<T> {
    public id: number;
    public func: ((...args: any[]) => T) | string;

    constructor(opts: ITask<T>) {
        this.id = opts.id;
        this.func = opts.func;
    }
}

class Runner {
    constructor() { }
    public run<T>(task: { func: T }, ...args: T extends (...args: infer Args) => any ? Args : any[]) {
        if (typeof task.func === 'function')
            return task.func(...args);
        else
            return eval(`(${task.func})`);
    }
}

const task = new Task({ id: 1, func: (x: number) => x + 1 });

const runner = new Runner();
// should be expecting an argument, but func is not inferring from usage it's a function and not a string
runner.run(task);

TS 游乐场链接

如果您希望编译器充分记住func属性的类型以了解它的参数是什么,则需要泛型类型参数来包含这些参数。 你的ITask<T>T只代表func的返回类型,如果它是一个函数,并且参数类型是any[] ,这根本不是通用的。 这是一种不同的可能类型,它能够跟踪函数参数和返回类型:

interface ITask<F extends string | ((...args: any) => any)> {
    id: number;
    func: F;
};

这里func属性只是泛型类型F ,它被限制为函数类型或字符串。

Task的定义相应地发生了变化:

class Task<F extends string | ((...args: any) => any)> implements ITask<F> {
    public id: number;
    public func: F
    constructor(opts: ITask<F>) {
        this.id = opts.id;
        this.func = opts.func;
    }
}

您的Runner类或多或少保持不变(尽管eval()使我感到害怕)。 你可能想也可能不想更强烈地输入它的返回值(这是之前的any ):

class Runner {
    constructor() { }
    public run<T>(
        task: { func: T },
        ...args: T extends (...args: infer Args) => any ? Args : any[]
    ): T extends (...args: any) => infer R ? R : any {
        if (typeof task.func === 'function')
            return task.func(...args);
        else
            return eval(`(${task.func})`);
    }
}

现在你得到了这种行为:

const task = new Task({ id: 1, func: (x: number) => x + 1 });
const runner = new Runner();
runner.run(task); // error! expected 2 arguments but got one
const numVal = runner.run(task, 10); // okay
// const numVal: number;

const anyVal = runner.run(new Task({ id: 2, func: "someString" })); //okay
// const anyVal: any;

这一切在我看来都是合理的。 好的,希望有帮助; 祝你好运!

Playground 链接到代码

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM