Reputation: 10561
In my functions.ts, I define 2 functions, both take an api
object, and returns a function with different arg types, arg numbers and return types.
export function f1 (api: Api) {
return function (a: number): number[] {/* not important */}
};
export function f2 (api: Api) {
return function (a: string, b: string): boolean {/* not important */}
};
Now given an api: Api
global object, I want to define an object, that has fields f1
and f2
, and the value associated to each field is the inner function inside the above 2 functions.
Namely, manually, I would do:
const api: Api = ...;
const myObject = {
f1: f1(api),
f2: f2(api),
}
And this works super well.
But the next step: I wish to do it dynamically, i.e. without manually typing f1 and f2.
Here's my start:
import * as functions from './functions';
const api: Api = ...;
const myObject = Object.keys(functions).reduce((accumulator, functionName) => {
accumulator[functionName] = functions[functionName](api);
}, {} as ???);
The code works, but the typings don't. I'm not sure what to put instead of ???
. {[index: keyof typeof functions]: (...args: any[]) => any}
would work, but I lose a lot of information on types.
I tried looking at TS's Parameters<T>
, ReturnType<T>
and I'm sure there's something to be done with infer
, but can't seem to get hold of it.
Upvotes: 1
Views: 56
Reputation: 141512
The code works, but the typings don't. I'm not sure what to put instead of ???.
Here is an approach that works. It creates a new type that maps each function name to its return type.
type ReturnTypes<T> = {
[K in keyof T]: T[K] extends (...args: any) => any
? ReturnType<T[K]>
: never;
};
const myObject = Object
.keys(functions)
.reduce(
(accumulator, functionName) => {
accumulator[functionName] = functions[functionName](api);
return accumulator;
},
{} as ReturnTypes<typeof functions>
);
const result1: number[] = myObject.f1(10);
const result2: boolean = myObject.f2('foo', 'bar');
Here it is in the TypeScript playground.
Upvotes: 1