function whatever(object, methodName, args) {
return object[methodName](...args);
}
可以键入以上内容,以便执行以下操作:
methodName
是object
的键。object[methodName]
是可调用的,其args为...args
。whatever(object, methodName, args)
的返回类型是object[methodName](...args)
的返回类型。我能找到的最接近的东西是function.apply
的定义,但与上面的定义并不完全相同。
答案 0 :(得分:30)
我认为这可以解决问题:
function callMethodWithArgs<
M extends keyof T,
T extends { [m in M]: (...args: Array<any>) => any },
F extends T[M]
>(obj: T, methodName: M, args: Parameters<F>) {
return obj[methodName](...args) as ReturnType<F>;
}
不过需要TS 3!
答案 1 :(得分:3)
type Dictionary = { [key: string]: any }
type MethodNames<T extends Dictionary> = T extends ReadonlyArray<any>
? Exclude<keyof [], number>
: { [P in keyof T]: T[P] extends Function ? P : never }[keyof T]
function apply<T extends Dictionary, P extends MethodNames<T>>(
obj: T,
methodName: P,
args: Parameters<T[P]>
): ReturnType<T[P]> {
return obj[methodName](...args);
}
// Testing object types:
const obj = { add: (...args: number[]) => {} }
apply(obj, 'add', [1, 2, 3, 4, 5])
// Testing array types:
apply([1, 2, 3], 'push', [4])
// Testing the return type:
let result: number = apply(new Map<number, number>(), 'get', [1])
Dictionary
类型允许使用T[P]
。
Parameters
和ReturnType
类型被烘焙到TypeScript中。
MethodNames
类型提取其值可分配给Function
类型的所有键。数组类型需要特殊情况。
答案 2 :(得分:2)
返回类型是否始终与someObject [methodName]相同?
function whatever<O extends {[key: string]: (...args) => R}, R>(object: O, methodName: keyof O, ...args: any[]): R {
return object[methodName](...args);
}
然后您可以执行此操作。
答案 3 :(得分:2)
这应该做到。这样会检查methodName
以及每个args
。
(注意:不完美,可以进行一些改进;例如unknown
-> any
)
type ArgumentsType<T> = T extends (...args: infer A) => any ? A : never;
function whatever<
T extends object,
TKey extends keyof T,
TArgs extends ArgumentsType<T[TKey]>
>(
object: T,
methodName: T[TKey] extends ((...args: TArgs) => unknown) ? TKey : never,
args: TArgs
): T[TKey] extends ((...args: TArgs) => unknown) ? ReturnType<T[TKey]> : never {
const method = object[methodName];
if (typeof method !== 'function') {
throw new Error('not a function');
}
return method(...args);
}
interface Test {
foo: (a: number, b: number) => number;
bar: string;
}
const test: Test = {
foo: (a, b) => a + b,
bar: 'not a function'
};
const result = whatever(test, 'foo', [1, 2]);
答案 4 :(得分:1)
怎么样?
function whatever(someObject: { [key: string]: Function}, methodName: string, args: any[]) {
return someObject[methodName](...args);
}
whatever({ func1: (args) => (console.log(...args)) }, 'func1', [1])
答案 5 :(得分:1)
到目前为止,我能想到的最接近的结果是
function whatever<T extends object>(object: T, methodName: keyof T, args: any) {
const func = object[methodName]
if (typeof func === "function") {
return func(...[args])
}
}
const obj = {
aValue: 1,
aFunc: (s: string) => "received: " + s
}
whatever(obj, "aFunc", "blabla")
可以正确检查作为对象一部分的键。
虽然仍然缺少返回类型和args的类型推断。如果找到更好的解决方案,我将更新答案。
答案 6 :(得分:1)
最安全类型的选项是使用Parameters
获取函数的参数类型(以使参数为安全类型),ReturnType
返回a的结果。功能。
您还需要添加一个类型参数来捕获传入的实际键,以便我们可以获取函数的实际类型(T[K]
(
function whatever<T extends Record<string, (...a: any[])=> any>, K extends keyof T> (someObject: T, methodName: K, ...args: Parameters<T[K]>) : ReturnType<T[K]>{
return someObject[methodName](...args);
}
whatever({ func1: (args: number[]) => (console.log(...args)) }, 'func1', [1])
whatever({ func1: (args: number[]) => (console.log(...args)) }, 'func1', ["1"]) // err
答案 7 :(得分:0)
可以满足所有条件,并且不需要类型断言。
type AnyFunction = (...args: any[]) => any;
function whatever<
T extends Record<PropertyKey, AnyFunction>,
K extends keyof T,
A extends Parameters<T[K]>
>(object: T, methodName: K, args: A): ReturnType<T[K]> {
return object[methodName](...args);
}
Parameters
类型是TypeScript 3.1之后的标准库的一部分。如果您使用的是旧版本,请自行创建:
type Parameters<T extends (...args: any[]) => any> =
T extends (...args: infer P) => any
? P
: never;
使用PropertyKey
类型而不是string
,可以使用类型string | number | symbol
的属性,这是JavaScript支持的全部色域。