Reputation: 6632
How can I write a return type annotation for a function that takes an object, calls all of its methods, and returns a new object with the original keys mapped to the methods' return values?
function callMethods<T>(obj: T) {
const objResults = {};
Object.keys(obj).forEach((prop) => objResults[prop] = obj[prop]({}));
return objResults;
}
type MethodArgs = any // some complex object
console.log(callMethods({
a: (_args: MethodArgs): number => 1,
b: (_args: MethodArgs): string => "one",
c: (_args: MethodArgs): number[] => [1]
}));
// => {a: 1, b: "one", c: [1]}
// This object's type should be {a: number, b: string, c: number[]}
Upvotes: 2
Views: 1711
Reputation: 8370
As of [email protected]
has been added mapped types and in [email protected]
conditional types and type inferrence. This one got really easy:
type MappedReturnTypes<
T extends Record<string, (...args: any[]) => any>
> = { [K in keyof T]: ReturnType<T[K]> }
Upvotes: 0
Reputation: 31612
Right now there is no way of properly retrieving the return type of a method call and as such my solution is only partial. However there is a proposal in the works and you can read more about it here.
The best you could is at least get some more typing out of the thing you have now.
One thing you can do is use mapped types in order to retrieve the keys from T and use them as keys in the return value.
function callMethods<T>(obj: T) {
return Object.keys(obj).reduce((previous, current) => {
previous[current] = obj[current]({});
return previous;
}, {} as {[P in keyof T]: any});
}
Because the return type of the methods can't be determined the value type of properties of the returned object is going to be any.
If the return types are finite you could define them as a type and use them (it's not perfect but it might be better).
type ReturnTypes = number | string | number[];
function callMethods<T>(obj: T) {
return Object.keys(obj).reduce((previous, current) => {
previous[current] = obj[current]({});
return previous;
}, {} as {[P in keyof T]: ReturnTypes});
}
If both the return types and the type of the object being passed in is known you could pass those as external parameters too, so you make a more generic function.
type ReturnTypes = number | string | number[];
interface Methods {
a: (args: any) => number,
b: (args: any) => string,
c: (args: any) => number[],
}
function callMethods<T, V>(obj: T) {
return Object.keys(obj).reduce((previous, current) => {
previous[current] = obj[current]({});
return previous;
}, {} as {[P in keyof T]: V});
}
let result = callMethods<Methods, ReturnTypes>({
a: (_args): number => 1,
b: (_args): string => "one",
c: (_args): number[] => [1]
});
While this isn't the perfect solution I hope it helps you.
Note: please forgive the rewrite of the method, it seemed cleaner to use reduce.
Upvotes: 3