[英]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);
如果您希望编译器充分记住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;
这一切在我看来都是合理的。 好的,希望有帮助; 祝你好运!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.