Call a member of an object with arguments
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])
Playground link
The Dictionary
type allows T[P]
to be used.
The Parameters
and ReturnType
types are baked into TypeScript.
The MethodNames
type extracts any keys whose value is assignable to the Function
type. Array types require a special case.
Checklist
- Method name is validated? ✅
- Arguments are type-checked? ✅
- Return type is correct? ✅
I think this does the trick:
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>;
}
Requires TS 3 though!