Is there a way to retrieve the ReturnType of functions based on a parameter list in Typescript?

I am struggling with defining a main function myMainFunction() that accepts a list of functions with different return types as parameters.

My goal is to have the return type of myMainFunction be determined by the return types of the functions passed to it as parameters. However, I am facing difficulties in implementing this.

Below is my current approach, which does not seem to be working as expected:

const funcA = async (): string => "Hello";
const funcB = async (): {name: string, age: number} => ({ name: "Jane", age: 123 });

const myMainFunction = async <F extends () => ReturnType<F>>(...functions: F[]): Promise<ReturnType<F>[]> => {
  const res = await Promise.all(functions.map((fn) => fn()));
  return res;
};

// This does not provide the expected result, as values has a type of Promise<string>[]
// I am anticipating values to be of type Promise<[string, {first: string, age: number }]>
const values = await myMainFunction(funcA, funcB);

The issue with the above code is that the variable values is of type Promise<string>[]. It seems to only consider the return type of the first function in the parameters.

My expectation is for values to have a type of

Promise<[string, {first: string, age: number }]>
, representing the return types of funcA() and funcB().

How can I achieve the desired outcome?

Answer №1

In Typescript, there is a unique type for Array types called T[number], allowing us to accomplish the following:

const funcA = async (): Promise<string> => "Hello";
const funcB = async (): Promise<{name: string, age: number}> => ({ name: "Jane", age: 123 });

const myMainFunction = async <T extends any[number][]> (...functions: T) => {
  const res = await Promise.all(functions);
  return res;
};

async function test() {
    const values = await myMainFunction(funcA(), funcB());
    // values[0]  string
    // values[1]  {name: string, age: number}
    console.log(values[1].age);
}

When passing functions, the map function loses its type. However, by using the as keyword, we can reassign them back. With the help of @T.J.Crowder's MapToUnpromisedReturnType type, we can achieve this:

const funcA = async (): Promise<string> => "Hello";
const funcB = async (): Promise<{name: string, age: number}> => ({ name: "Jane", age: 123 });

type MapToUnpromisedReturnType<T extends Array<any>> = {
    [K in keyof T]: Awaited<ReturnType<T[K]>>;
};

const myMainFunction = async <T extends (() => Promise<any[number]>)[]> (...functions: T) => {
  const mapped = await functions.map(f => f());
  return mapped as MapToUnpromisedReturnType<T>;
};

async function test() {
    const values = await myMainFunction(funcA, funcB);
    // values[0]  string
    // values[1]  {name: string, age: number}
    console.log(values[1].age);
}

Here is a slightly refactored version without the helper:

const myMainFunction = async <T extends (() => Promise<any[number]>)[]> (...functions: T) => {
  return functions.map(f => f()) as {[K in keyof T]: Awaited<ReturnType<T[K]>>};
};

Answer №2

An outdated and unclean version - not recommended for use.

type ResolveFunction<T extends (...args: any) => any> = Awaited<ReturnType<T>>;
type ResolveFunctions<T extends [...((...args: any) => any)[]]> = T extends [
  infer Head,
  ...infer Tail
]
  ? Head extends (...args: any) => any
    ? Tail extends ((...args: any) => any)[]
      ? [ResolveFunction<Head>, ...ResolveFunctions<Tail>]
      : []
    : []
  : [];

const myMainFunction = async <T extends [...((...args: any) => any)[]]>(
  ...functions: T
): Promise<ResolveFunctions<T>> => {
  const res = await Promise.all(functions.map((fn) => fn()));
  return res as any;
};

A newer version inspired by another solution.

type AsyncFunction = (...args: any) => Promise<any>;
type ResolveFunction<T extends AsyncFunction> = Awaited<ReturnType<T>>;
type ResolveFunctions<T extends AsyncFunction[]> = {
  [K in keyof T]: ResolveFunction<T[K]>;
};

const myMainFunction = async <T extends AsyncFunction[]>(
  ...functions: T
): Promise<ResolveFunctions<T>> => {
  const res = await Promise.all(functions.map((fn) => fn()));
  return res as any;
};

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

Unusual conduct when employing basic directive for validation messages

Within my code, I have implemented the following directive: App.directive("validateMsgFor", function(){ return{ templateUrl : "view/templates/validateMsgFor.html", restrict: "E", scope: { field : "=" } ...

Communicating between PHP chat client and server

Currently, I am developing a basic PHP web chat application that interacts with a MySQL database. The communication is facilitated through AJAX requests - when a user posts a message, it gets saved in the database. function sendData(){ var textData = $(& ...

Modify the website address and show the dynamic content using AJAX

$(function(){ $("a[rel='tab']").click(function(e){ //capture the URL of the link clicked pageurl = $(this).attr('href'); $("#Content").fadeOut(800); setTimeout(function(){ $.ajax({url:pageurl+'?rel=tab&apo ...

Arranging data in a table using PHP

After some thorough research, I am finally prepared to dive into the world of PHP. My project involves an HTML5 animation with table sorting functionalities - one button for sorting by date and another for sorting by title. Despite my efforts to find a cus ...

Dragging the world map in D3 causes it to appear jumpy and erratic

I'm currently working on a Vue project to create an interactive world map that allows users to drag and zoom. I've attempted to integrate D3 for this purpose, but encountered an issue where the map jumps to the bottom right of the page whenever I ...

Automated Menu Selection using Selenium

I'm currently facing a challenge in writing a Python script using Selenium to interact with a webpage. I am struggling to use the .click() method to select an expandable list on the page. Despite successfully logging in and navigating to the desired p ...

Developing an Easy-to-Use PDF Popup Interface

I need a simple PDF modal where I can input the text: 'The sky is blue' and have it link to a PDF that opens up when clicked. I found a similar sample online, but it includes an image plus an image modal that pops up. I want to replace the imag ...

Stop HTML audio playback upon clicking on a specific element

I'm looking to add background music to my website with a twist - a music video that pops up when the play button is clicked. But, I need help figuring out how to pause the background music once the user hits play. Play Button HTML - The play button t ...

Guide on adding HTML to a specific div element in jQuery by utilizing the keyword "(this)"

I have been working on a script for my PHP page in Wordpress to add titles to images in my photo gallery that open in a lightbox. Here's what I developed: $(".responsive1").bind("click", function() { $(this).("<div class='imgTitle ...

From creating a simple jQuery fiddle, let's delve into the world

Here is a code snippet I'm trying to transition from jQuery to an Angular directive. Visit this link to view the original code: http://jsfiddle.net/rhtr1w04/ Below is my implementation in app.js: angular.module('app',[]).directive('an ...

A guide on extracting text enclosed by <a> and </a> tags using regular expressions

I have come across the following code: <a align="center" href="http://google.com"><b>Google Link<b></b></a> <a align="center" href="http://yahoo.com"><strong>Yahoo Link</strong></a> Now, I am looking ...

Encountering an ENOENT error in the CodeSandbox CI environment, whereas this issue does not arise in GitHub

I am currently in the process of creating a GitHub pull request for the react-querybuilder library. However, I am encountering an issue with my CodeSandbox CI job, which is failing and displaying the following error message: { [Error: ENOENT: no such file ...

Module fails to load in the proper sequence

As a .NET developer who is relatively new to modern client-side web applications, I am currently working on developing an Angular2 charting application using Chart.js. The modules are being loaded with SystemJS. Below is the content of my systemjs.config. ...

Having trouble with uploading an image in Laravel using a modal?

For my laravel inventory system, I am facing an issue with uploading images for products. The old code I used for image upload is not working in my current project where I am utilizing a modal and ajax request to save inputs in the database. Can anyone pro ...

Issue with iOS 10: Changes to class not reflected in CSS/styles

Currently, I am utilizing Ionic with Angular to develop an application for Android and iOS. Surprisingly, everything functions smoothly on Android, but there seems to be an issue with iOS. I am employing a class change on an element using ng-class. I can ...

What is your strategy for managing errors when utilizing xui's xhr functionality?

Recently, I've been utilizing the code below to extract data from an external source. <script type="text/javascript"> xui.ready(function() { var url = 'http://www.domain.com/getData.aspx'; x$().xhr(url, ...

Can you show me the steps for linking the next method of an EventEmitter?

After emitting an event, I am looking to run some additional code. Is there a method to chain the .next() function in this way? @Output() myEvent = new EventEmitter<string>(); this.myEvent.next({‘test string’}).onComplete(console.log('done& ...

Connect the keys from one enum to either keys or values in another enum

When working with the code below, it is important that the keys of PropertiesNamesInDataBase align with those in User.Keys. While the values of PropertiesNamesInDataBase are used in the backend, it is crucial for uniformity that the names match in the fron ...

Display elements exclusively when the class XY is in an active state using JavaScript

Hello everyone, I'm new to this platform and excited to share my first post. Currently, I find myself facing a major challenge as I navigate through a bootcamp program. I have been working on a website with different sections that require specific fu ...

What causes React Router to render the Navigation to the <Home/> component when the userData is still available, but redirects to "/login" otherwise?

I'm currently developing a Note-taking application using React with React Router for navigation. For creating routes, I am utilizing createBrowserRouter(). Below is the code snippet: import './App.css'; import NoteList from "./componen ...