Create a TypeScript function that takes multiple functions as parameters and returns a union type consisting of the return values of each function

Would greatly appreciate any assistance with adding types to the following JavaScript function in TypeScript. I've been trying to solve this without resorting to using 'any' for an entire day with no luck.

Here's the JavaScript function:

function executeAll(...funcs) {
  const result = funcs.reduce((currentResult, nextFunc) => {
    return {
      ...currentResult,
      ...nextFunc(),
    };
  }, {});

  return result;
}

Here's an example of how to use it:

const result = executeAll(
    () => { return { firstResult: "Abc" } },
    () => { return { secondResult: 123 } });
  
console.log(result);

The function in the example creates an object like this: { firstResult: "Abc", secondResult: 123 }

Essentially, the result of "executeAll" should be a union of the return values of each function passed in. Is this even possible, or am I attempting something that cannot be achieved?

Any guidance would be highly appreciated.

Thank you,

Miro

Answer №1

To achieve this functionality, utilize the Spread type as described in this thread along with a recursive type implementation:

// =========
// Spread type
// =========

// Identifies property names in T with types including undefined
type OptionalProperties<T> =
  { [K in keyof T]: undefined extends T[K] ? K : never }[keyof T];

// Merges common properties from L and R, replacing undefined in R[K] with type in L[K]
type SpreadProperties<L, R, K extends keyof L & keyof R> =
  { [P in K]: L[P] | Exclude<R[P], undefined> };

type Identity<T> = { [K in keyof T]: T[K] } // see note below*

// Type of { ...L, ...R }
type Spread<L, R> = Identity<
  // Properties in L not found in R
  & Pick<L, Exclude<keyof L, keyof R>>
  // Properties in R with types excluding undefined
  & Pick<R, Exclude<keyof R, OptionalProperties<R>>>
  // Properties in R, including undefined types, not existing in L
  & Pick<R, Exclude<OptionalProperties<R>, keyof L>>
  // Properties in R, including undefined types, found in L
  & SpreadProperties<L, R, OptionalProperties<R> & keyof L>
>;

// =========
// Actual type
// =========

type FunctionType = (...args: any) => any;

type MergeFunctions<T extends FunctionType[]> =
    T extends [infer H, ...infer R]
        ? H extends FunctionType ? R extends FunctionType[]
            ? Spread<ReturnType<H>, MergeFunctions<R>>
            : never : never
        : {};

function performAll<T extends FunctionType[]>(...functions: T): MergeFunctions<T> {
  const result = functions.reduce((currentResult, nextFunc) => {
    return {
      ...currentResult,
      ...nextFunc(),
    };
  }, {});

  return result as MergeFunctions<T>;
}

const finalResult = performAll(
    () => { return { firstResult: "Abc" } },
    () => { return { secondResult: 123 } });

type Bar = typeof finalResult;

Link to Playground

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the best way to assign a value to a class variable within a method by referencing the 'this' keyword?

Is there a way to set the state of this Ionic react app when displaying the outcome of a reset service? I am facing challenges with using this.setState({resetSuccess}) within a method due to scope issues. (Details provided in comments) Here is the relevan ...

Conditioning types for uninitialized objects

Is there a way to create a conditional type that can determine if an object is empty? For instance: function test<T>(a: T): T extends {} ? string : never { return null } let o1: {} let o2: { fox? } let o3: { fox } test(o1) ...

I am facing an issue with the Angular2 Modal Form where it only displays the data but does

Hey there, I recently started diving into Angular and I'm loving the learning process. Currently, I've managed to successfully load a form into my Modal when clicking on "viewDetails". However, as soon as I modify the Form from <form ngNoFo ...

Naming convention for TypeScript accessors

Expanding on the previous solution When I convert the example object to JSON from the answer above: JSON.stringify(obj) The output is: {"_id":"3457"} If I intend to transmit this data over a service and store it in a database, I prefer not to use the ...

Retrieve the data from the mat-checkbox

My goal is to retrieve a value from a mat-checkbox, but the issue is that we only get boolean expression instead of the string value. Here's an example snippet of what I'm looking for: <mat-checkbox formControlName="cb2" <strong&g ...

Having trouble with the npm Fluid Player installation

I am attempting to integrate Fluid Player into my Angular application Using - npm i fluid-player However, I'm encountering this error ...

TS2322 error: Attempting to assign type 'any' to type 'never' is invalid

Currently, I am utilizing "typescript"- "3.8.3", and "mongoose": "5.9.11". Previously, my code was functional with version "typescript": "3.4.x", and "mongoose": "4.x". Here is a snippet of my code: https://i.stack.imgur.com/j3Ko2.png The definition for ...

"Utilizing ReactJS and Typescript: A guide on initiating a Redux dispatch event through an axios

Looking for help with ReactJS typescript and redux dispatch events when calling APIs using axios interceptors? Check out my code snippet below. Codesandbax Repo App.tsx import "./App.css"; import "bootstrap/dist/css/bootstrap.min.css" ...

Attempting to leverage the combination of mocha, ES6 modules, and ts-node while utilizing the --experimental-loader option

I've been attempting to make the ts-node option --experimental-loader function alongside mocha, but so far I haven't had any success. Before I started compiling ES6 modules, running mocha tests was as simple as: "test": "nyc --reporter=html mocha ...

Error message: "Supabase connection is returning an undefined value

I am encountering an issue with my Vercel deployed Remix project that utilizes Supabase on the backend, Postgresql, and Prisma as the ORM. Despite setting up connection pooling and a direct connection to Supabase, I keep receiving the following error whene ...

Error thrown by webpack: Module 'pug' not found when attempting to access get-api

After setting up webpack in express, a new folder was created. When I try to run bundle.js, it shows the message "server is running on port 3000". However, when I access the API at http://localhost:3000/api/test, the whole bundle.js loads in the console an ...

Error: The window object is not defined in NextJS

I've encountered an issue while trying to build the app for production. The error message states: ReferenceError: window is not defined. I'm struggling to find a solution. FullCode: const [windowSize, setWindowSize] = useState<WindowInfo>( ...

Mastering the art of connecting content within Prismic

I have been working on creating a mega menu for my website header, but I am encountering a type error. Has anyone else faced this issue before and how did you resolve it? I am currently importing the generated types from @/prismicio-types. Here is an exam ...

Angular 8 does not show the default option in the select tag

When I use the following code snippet: <div style="text-align:center"> <form> <select type="checkbox" name="vehicle1" (change)="onchange()" > <option> 1 </option> <opti ...

The getStaticProps() function in NextJS has not been invoked

Currently, I am working on a basic website and my goal is to retrieve data from an API and showcase it on my component. However, I've encountered an issue where the getStaticProps() method doesn't seem to be triggering. Below is the code snippet ...

Tips for effectively managing index positions within a dual ngFor loop in Angular

I'm working on a feedback form that includes multiple questions with the same set of multiple choice answers. Here's how I've set it up: options: string[] = ['Excellent', 'Fair', 'Good', 'Poor']; q ...

Displaying multiple lines in an alert box using Angular 8

I need assistance in displaying an alert message when the user selects a checkbox. We have a shared alert service component that is being utilized by every module. My current code snippet is as follows: if(this.checkboxvalue) { this.al ...

Observable<void> fails to trigger the subscriber

I am currently facing a challenge with implementing a unit test for an Observable in order to signal the completion of a process. While there is no asynchronous code in the logout function yet, I plan to include it once the full logic is implemented. The m ...

Using styled-components to enhance an existing component by adding a new prop for customization of styles

I am currently using styled-components to customize the styling of an existing component, specifically ToggleButton from material ui. However, I want my new component to include an additional property (hasMargin) that will control the style: import {Toggle ...

Navigating through the keys of a parameter that can assume one of three distinct interfaces in TypeScript: a guide

Here is a function example: function myFunc(input: A | B | C) { let key: keyof A | keyof B | keyof C; for(key in input) { let temp = input[key]; console.log(temp); } } The definitions for A, B, and C are as follows: interfa ...