Home > Enterprise >  how to aggregate all param types of an object which is an aggregation of functions?
how to aggregate all param types of an object which is an aggregation of functions?

Time:03-24

Please take a look at this typescript code :

type params1 = {p:number} ;
type params2 = {p:boolean};
type params3 = {p:string};
type res = {r:string};

const fObject = {
a:(a:params1):res=>{return {r:`${a.p}`}},
b:(a:params2):res=>{return {r:`${a.p?1:0}`}},
c:(a:params3):res=>{return {r:a.p}}
}

How can we create a type that looks like this:

type params = params1 | params2 | params3

I'm creating a new object that will be all the functions of fObject but will run a logger function on the result of each function

like so:

const loggingObject: typeof fObject = Object.keys(fObject).reduce(
 (result: any, key: string) => {
   result[key] = (args: any/*reason for Q*/) => logger.report(fObject
  [key as loggingObject](args /*If i put something like 2 it says it has no properties in common with params1 & param2 & param3, thats why I know its possible*/));
   return result;
  },
  {},
);

I don't want the any type and need a dynamic type that can take an object with function and give a union of all param types of all the function in the object

CodePudding user response:

Great question. This is possible--in multiple ways! In this solution we will use the following techniques:

The links might be helpful if you want to drill down a bit and if my explanations are not coherent enough. In the end we will have a type that works like so:

// your code here...
type MyParams = ParamForKeys<typeof fObject>;
// => `type MyParams = param1 | param2 | param3`

So what we're going to do is to map over the keys of fObject, and replace each key with the argument type of its function. Mapping over keys looks like this:

type MyParams<T> = { [key in keyof T]: ... }

Now we want to extract the argument type. This will use Conditional Types. Conditional types allow us to do conditions, as well as extract an argument by using inference, with the infer key:

type UnwrapPromise<T> = T extends Promise<infer Inner> ? Inner : never;
type A = UnwrapPromise<Promise<number>> // => number
type B = UnwrapPromise<number> // => never

And that's the exact method we're going to use:

type GetFirstParam<T> = T extends ((param: infer Param) => any) ? Param : never

And now if we build this together we will get:

type GetFirstParam<T> = T extends ((param: infer Param) => any) ? Param : never
type ParamForKeys<T> = { [key in keyof T]: GetFirstParam<T[key]> }

but that gives us the following type:

type MyParams = ParamForKeys<typeof fObject>;
// { a: param1, b: param2, c: param3 }

To get the values back in a union format (param1 | param2 | param3) we can access them like so:

type GetFirstParam<T> = T extends ((param: infer Param) => any) ? Param : never
type ParamForKeys<T> = { [key in keyof T]: GetFirstParam<T[key]> }[keyof T]

And that's the solution. Keep in mind that I'm not sure that this solution is what you're looking for: having the {a: param1, b:param2} makes more sense to me in terms of your logging situation, but that's the solution for you question!

Hope that it was clear. You can see it live in this TypeScript playground.

  • Related