Reputation: 137
May I please ask if anyone is able to help me with adding types to javascript function below in typescript? In best case scenario I would like to avoid using 'any' but I was not able to figure this out even after whole day of googling.
Function in javascript:
function executeAll(...funcs) {
const result = funcs.reduce((currentResult, nextFunc) => {
return {
...currentResult,
...nextFunc(),
};
}, {});
return result;
}
Example of usage:
const result = executeAll(
() => { return { firstResult: "Abc" } },
() => { return { secondResult: 123 } });
console.log(result);
Function in example creates object like this: { firstResult: "Abc", secondResult: 123 }
Basically the result of "executeAll" should be UNION of return values of each function passed in. Is something like that even possible or I am trying to achieve something that can not be done?
Any help would be much appreciated.
Thanks,
Miro
Upvotes: 1
Views: 55
Reputation: 35560
You can do this with the Spread
type outlined in this post as well as a recursive type:
// =========
// spread type
// =========
// Names of properties in T with types that include undefined
type OptionalPropertyNames<T> =
{ [K in keyof T]: undefined extends T[K] ? K : never }[keyof T];
// Common properties from L and R with undefined in R[K] replaced by type in L[K]
type SpreadProperties<L, R, K extends keyof L & keyof R> =
{ [P in K]: L[P] | Exclude<R[P], undefined> };
type Id<T> = {[K in keyof T]: T[K]} // see note at bottom*
// Type of { ...L, ...R }
type Spread<L, R> = Id<
// Properties in L that don't exist in R
& Pick<L, Exclude<keyof L, keyof R>>
// Properties in R with types that exclude undefined
& Pick<R, Exclude<keyof R, OptionalPropertyNames<R>>>
// Properties in R, with types that include undefined, that don't exist in L
& Pick<R, Exclude<OptionalPropertyNames<R>, keyof L>>
// Properties in R, with types that include undefined, that exist in L
& SpreadProperties<L, R, OptionalPropertyNames<R> & keyof L>
>;
// =========
// actual type
// =========
type Func = (...args: any) => any;
type FuncMerge<T extends Func[]> =
T extends [infer H, ...infer R]
? H extends Func ? R extends Func[]
? Spread<ReturnType<H>, FuncMerge<R>>
: never : never
: {};
function executeAll<T extends Func[]>(...funcs: T): FuncMerge<T> {
const result = funcs.reduce((currentResult, nextFunc) => {
return {
...currentResult,
...nextFunc(),
};
}, {});
return result as FuncMerge<T>;
}
const result = executeAll(
() => { return { firstResult: "Abc" } },
() => { return { secondResult: 123 } });
type Foo = typeof result;
Upvotes: 1