What is the method for assigning the results of Array#map to a variadic tuple `[P in keyof T]: ReturnType<T[P]>`?

Attempting to devise a strongly-typed function for mapping over a uniform array of functions that yield arbitrary values.

I've tinkered with both approaches, and while they do produce the expected types, I encounter issues with the return value:

const executeFunctions = <T extends (() => any)[]>(
    functionsArray: [...T],
): {
    [P in keyof T]: ReturnType<T[P]>
} => {
    const result = [];
    for (let i = 0; i < functionsArray.length; i += 1) {
        result.push(functionsArray[i]());
    }
    return result;
}
// ^ Error: Type 'any[]' is not assignable to type '{ [P in keyof T]: ReturnType<T[P]>; }'.ts(2322)

const results = executeFunctions([
    () => 'hello',
    () => 123,
    () => true,
]);

// const results: [string, number, boolean]

const executeFunctions = <T extends (() => unknown)[]>(
    tasks: [...T],
): {
    [P in keyof T]: ReturnType<T[P]>;
} => tasks.map(task => task());

// ^ Error: Type 'unknown[]' is not assignable to type '{ [P in keyof T]: ReturnType<T[P]>; }'.ts(2322)


const results = executeFunctions([
    () => 'hello',
    () => 123,
    () => true,
]);
// const results: [string, number, boolean]

How can I accurately determine the return type?

Answer №1

To achieve this, you can explicitly convert the unknown[] array returned by .map to match the complex return type of the function runAll. Although it may seem unconventional, performing a cast in this situation is safe because you are certain about the types involved. In my adaptation based on your second example, I also addressed an additional issue related to the return type. It resulted in:

type AllResults<T> = {
    [P in keyof T]: T[P] extends (() => unknown) ? ReturnType<T[P]> : never;
};

const runAll = <T extends Array<() => unknown>>(
    actions: [...T],
): AllResults<T> => actions.map(action => action()) as AllResults<T>;

const results = runAll([
    () => 'hello',
    () => 123,
    () => true,
]);
// const results: [string, number, boolean]

Answer №2

Let's explore the following scenario:


type Fn = () => any

type MapPredicate<F> = F extends Fn ? ReturnType<F> : never

/**
 * A function that iterates through an array and retrieves the return type
 */
type Mapped<
  Arr extends Array<unknown>,
  Result extends Array<unknown> = []
  > = Arr extends []
  ? []
  : Arr extends [infer H]
  ? [...Result, MapPredicate<H>]
  : Arr extends [infer Head, ...infer Tail]
  ? Mapped<[...Tail], [...Result, MapPredicate<Head>]>
  : Readonly<Result>;

/**
 * Function overload to handle different callbacks in an array
 * check out documentation for more details: 
 * https://www.typescriptlang.org/docs/handbook/functions.html#overloads
 */
function runAll<Cb extends Fn, Cbs extends Cb[]>(
  array: [...Cbs],
): Mapped<Cbs>

function runAll<Cb extends Fn, Cbs extends Cb[]>(
  array: [...Cbs],
) {
  return array.reduce<ReadonlyArray<ReturnType<Cbs[number]>>>(
    (acc, elem) => [...acc, elem()],
    []
  )
}

const results = runAll([
  () => 'hello',
  () => 123,
  () => true,
]); // [string, number, boolean]

Playground

When working with functions that iterate through arrays, specifying a return type explicitly may not be effective. It is advisable to use overloads in such scenarios.

I have introduced a helper called Mapped, which recursively traverses the type of the array argument and returns the return type of each callback function.

This is similar to array.map(elem=>elem())

For more intriguing examples, you can visit my blog here.

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

Ruby - Obtain the byte array that includes the two's complement representation of a Bignum/Fixnum

I am on a mission to create a byte array that contains the two's-complement representation of a Bignum or Fixnum in Ruby. In Java, there is a method that accomplishes this perfectly - Check out the Docs: Java toByteArray() method, and you can find the ...

Using the foreach loop to iterate through a System.String[] and print out the values

My current objective is to retrieve the IP addresses along with the NIC names. Below is a snippet of my code where I am trying to access the value of the IPAddress object, which is represented as type System.String[]. Despite referring to several online re ...

What are the reasons behind the code not functioning properly while utilizing 'vue-property-decorator' in Vue.js with TypeScript?

I am currently working on a project using Vue.js and TypeScript, but I am facing an issue with 'vue-property-decorator'. I have two code snippets that are supposed to achieve the same result, but only one is working. Can someone please help me id ...

Adding Components Dynamically to Angular Parent Dashboard: A Step-by-Step Guide

I have a dynamic dashboard of cards that I created using the ng generate @angular/material:material-dashboard command. The cards in the dashboard are structured like this: <div class="grid-container"> <h1 class="mat-h1">Dashboard</h1> ...

How can we ensure in ReactJS that one API call has been completed before making another one?

How can we ensure one API call is completed before making the next call in reactJS? In my componentDidMount function, I need to check the length of data. When the length is more than 4, I first want to save the data and then retrieve it. componentDidM ...

Add a consecutive number before each element in an array using Bash

In Bash, is there a simple and efficient method to add sequential numbers before each element in an array? Note: Commas have been added below for readability! For example, given the array: my_array=(a, b, c, d, e) The desired result is: my_array=(1, a, ...

What is the best way to expose the "nuxtServerInit" action for Nuxt.js when using dynamic modules exclusively?

According to this answer, the code snippet below is taken from the official documentation of vuex-module-decorators // @/store/index.ts import Vuex from 'vuex' const store = new Vuex.Store({ /* Ideally if all your modules are dynamic then ...

Achieving top-tier efficiency in segmenting items within an array of objects

Looking for a way to decrease the size of an array directly on my NodeJS server's memory. I aim to optimize network traffic by sending only the essential 'header' details of each object within the array. The current array on the server look ...

How to utilize Enzyme to call a React prop in TypeScript

I'm currently in the process of converting my Jest tests from Enzyme to TypeScript, and I've come across a specific case that I'm unsure how to resolve. Essentially, I'm attempting to invoke a function passed as a prop to a sub-componen ...

How to Retrieve Multiple Toggle Button Values using ID in AngularJS?

I am looking to retrieve the value of a toggle button as either yes or no. Since numerous questions are being generated dynamically, I need to determine this based on item.id. I am utilizing Angular and need to implement the logic in a TS file. Any assista ...

What programming language is the best choice for Angular 2 development?

As someone who is new to Angular 2, I've discovered that developers have the option to use TypeScript, ES6, and ES5 for their development needs. I understand that TypeScript is considered the superset of ES6 and ES5. Given the stark differences in sy ...

correct usage of getServerSideProps with Typescript in a next.js project using i18n

I'm encountering challenges with integrating next-i18next into a Typescript NextJS project. There are very few recent examples available for reference. I have successfully set up internationalized routing, but I am facing difficulties in configuring i ...

How can I calculate multiplication using values from dynamic text fields with Jquery/Javascript?

I am looking to create a functionality where textfield1 and textfield2 values are multiplied together to get the result displayed in textfield3. Here is an example scenario with 4 textfields: textfield1 * textfields2 = txtfield3 textfield1 * textfield2 ...

Include a character in a tube using Angular

Hey everyone, I have a pipe that currently returns each word with the first letter uppercase and the rest lowercase. It also removes any non-English characters from the value. I'm trying to figure out how to add the ':' character so it will ...

How can I pass a ref to a custom component in React with TypeScript using RefForwardingComponent and forwardRef?

I'm attempting to pass a reference to a custom component in order to set focus to that component. However, I am encountering the following error: const RefComp: React.RefForwardingComponent<HTMLInputElement, Props> Type '{ value: string; ...

Initializing Arrays of Struct in C++

Although I am not very familiar with coding in C++, I have encountered a simple problem that I cannot seem to solve due to incorrect syntax. My goal is to declare an array of a struct within one method, and then pass this array to another method where the ...

Using Angular to include more than two parameters in an HTTP GET request

Currently, I am developing an application that requires downloading a file upon clicking a button within a template. The screen displays multiple files, each with its own corresponding button. I need to send the index number of the array to Angular and pas ...

How can I showcase array elements using checkboxes in an Ionic framework?

Having a simple issue where I am fetching data from firebase into an array list and need to display it with checkboxes. Can you assist me in this? The 'tasks' array fetched from firebase is available, just looking to show it within checkboxes. Th ...

Recording attributes in a React component with Typescript

Is there a way to efficiently document React component props so that the comments are visible in the component's documentation? For example, consider this component: type TableProps = { /** An array of objects with name and key properties */ colu ...

In Swift 1.2, the dictionary keys and values are stored in the same order based on

Trying out some code here! let dict: [String:String] = ["1":"one","2":"two","3":"three","4":"four"] let keyArray = [String](dict.keys) let valueArray = [String](dict.values) println(dict) println(keyArray) println(valueArray) Hoping for the following re ...