Supply type Parameters<T> to a function with a variable number of arguments

I am interested in utilizing the following function:

declare function foo(...args: any): Promise<string>;

The function foo is a pre-defined javascript 3rd party API call that can accept a wide range of parameters.

The objective is to present a collection of potential function types to be passed into a wrapper function, which will help enforce the parameter types of this function. This generic wrapper function will take a type parameter representing another function and use its parameters and return type to determine the parameters and return type of the actual function invocation.

async function fooWithType<T extends (...args: any) => string>(args: Parameters<T>): Promise<string> {
    return await foo(...args)
}

Below is an example of one potential function type definition and how it could aid in invoking this 3rd party function through the wrapper function:

type funType = (a: string, b: boolean, c: number) => void;

fooWithType<funType>(["", true, 4])

However, I encountered an error indicating that I cannot use the spread operator on args?: Parameters<T>.

Type 'Parameters<T>' must have a '[Symbol.iterator]()' method that returns an iterator.

Why does the spread operator not work as expected here? I assumed I would be able to spread the tuple argument into the vararg parameter of the foo function.

Playground link

Answer №1

It appears that there is a mistake in your second function definition where you are missing brackets. When using ...args as an array, it should be defined as any[], not just any.

Try it out on the playground.

declare function foo(...args: any): Promise<any>;

// Corrected
async function fooWithType<T extends (...args: any[]) => any>(args: Parameters<T>): Promise<ReturnType<T>> {
    return await foo(...args)
}

type funType = (a: string, b: boolean, c: number) => void;

fooWithType<<funType>(["", true, 4])
// ^? 
// function fooWithType<funType>(args: [a: string, b: boolean, c: number]): Promise<void>

Edit:

If you want to pass an array as a "single" parameter, you have two options:

  • Adjust the funType type to accept a tuple (fixed-size array) instead of separate arguments
  • Use spread syntax when calling the function with an array
declare function foo(...args: any): Promise<any>;

async function fooWithType<T extends (...args: any[]) => any>(...args: Parameters<T>): Promise<ReturnType<T>> {
    return await foo(...args)
}

type funType = (a: string, b: boolean, c: number) => void;

fooWithType<<funType>(...["", true, 4]) // Spread tuple elements as separate parameters
// ^?
// function fooWithType<funType>(a: string, b: boolean, c: number): Promise<void>

Answer №2

A well-known issue in TypeScript is that the Parameters<T> for a generic type T constrained to (...args: any) => any is not recognized as spreadable. Check out more details on microsoft/TypeScript#36874.

An alternative solution would be to use the variadic tuple type mentioned in the TypeScript 4.0 release notes: [...Parameters<T>]

async function fooWithType<T extends (...args: any) => any>(
    args: [...Parameters<T>]
): Promise<string> {
    return await foo(...args);
}

Another workaround is modifying the constraint of T to use any[] instead of any for the argument type:

async function fooWithType<T extends (...args: any[]) => any>(
    args: Parameters<T>
): Promise<string> {
    return await foo(...args);
}
    

Given the loose typing of foo(), you could go with using a type assertion inside the function to suppress the error:

async function fooWithType<T extends (...args: any) => any>(
    args: Parameters<T>
): Promise<string> {
    return await foo(...args as any);
}

For further experimentation, you can check out this code on the TypeScript playground: Playground link

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

Error encountered in azure devops preventing successful execution: "npm ERR! code ELIFECYCLE"

I am facing an issue with my Azure DevOps build pipeline that contains 2 npm tasks: - one for npm install - and the other for npm run-script build Unfortunately, I am encountering errors that do not provide sufficient information about the root cause of ...

Having trouble selecting all checkboxes in the tree using angular2-tree when it first initializes

My goal is to have all checkboxes auto-checked when clicking the "feed data" button, along with loading the data tree. I've attempted using the following code snippet: this.treeComp.treeModel.doForAll((node: TreeNode) => node.setIsSelected(true)); ...

How to efficiently update a child component in React using UseState and establish a connection back to the parent component

I am currently working on developing a prototype for a master/detail scenario in React and Material-UI. The task involves creating a basic list of objects with the ability to edit and save an item using a dialog. While I have successfully updated the visit ...

Establishing a Next.js API endpoint at the root level

I have a webpage located at URL root, which has been developed using React. Now, I am looking to create an API endpoint on the root as well. `http://localhost:3000/` > directs to the React page `http://localhost:3000/foo` > leads to the Next API end ...

What is the best approach to incorporate Column Reordering in react-data-grid?

Within my REACT application, I have incorporated the npm package react-data-grid. They offer a sample showcasing Column Reordering on their website. I wish to replicate this functionality in my own code. Upon reviewing their source code, I am puzzled abou ...

Manipulating array objects by replacing values in Typescript

Attempted two different methods to obtain a partial summary within each array object, but unfortunately, both were unsuccessful. var arr = [ { "value": 10, "newBalance": 0 }, { "value": -10, "newBalance": 0 }, ...

What causes parameters to be undefined when making a DELETE request in my Next.js application running on version 14.1.4?

I am encountering an issue with my DELETE mapping export async function DELETE({params} : {params: {id: string}}) { try { const loanToDelete = await prisma.loan.findUnique({ where: { id: parseInt(params.id) } }) if (!loanToDelete ...

Instructions for designing a Loading Indicator or Progress Bar within the App Directory of NextJS

Having built a substantial web application using NextJS 13, I initially utilized the Pages Router. However, as I neared completion of the website, I decided to make the switch to the App Directory. The primary motivation behind this migration was not just ...

Customize Monaco Editor: Implementing Read-Only Sections

I am currently working on setting up the Monaco Editor so that specific sections of the text content are read-only. Specifically, I want the first and last lines to be read-only. See example below: public something(someArgument) { // This line is read-onl ...

React-table fails to show newly updated data

I am facing an issue with my react-table where real-time notifications received from an event-source are not being reflected in the table after data refresh. https://i.stack.imgur.com/q4vLL.png The first screenshot shows the initial data retrieval from th ...

ts-node: The colon symbol was not expected in this context

As I work on developing a backend server for my application, I made the decision to switch from using babel-node as the executor to utilizing ts-node. The command defined in my package.json file is: "server": "cd server && ts-node --project tsconf ...

Using Typescript for AngularJS bindings with ng.IComponentController

Currently, I am utilizing webpack alongside Babel and Typescript Presently, the controller in question is as follows: // HelloWorldController.ts class HelloWorldController implements ng.IComponentController { constructor(private $scope: ng.IScope) { } ...

Mastering the mapping function in ReactJs for a Map<string, boolean> data structure

Just a quick question, I seem to be stuck on this. Here is my Map: public checkboxOptions: Map<string, boolean>; In the render() function, I want to loop through it like this: renderCheckboxMenu(): any { let menu = <div className={`${style[ ...

Angular rxjs Distinctions

Coming from AngularJS to Angular, I'm still trying to wrap my head around rxjs observable. For example: User.ts export class User { id?:any; username:string; password:string; } Using <User[]> myUser(header: any) { const url = `${this.mainUr ...

How can you define the types of function arguments when destructuring arguments in TypeScript?

TS throws an error that states: Error:(8, 20) TS7031: Binding element 'on' implicitly has an 'any' type. Error:(8, 24) TS7031: Binding element 'children' implicitly has an 'any' type. Below is the function I am wor ...

Running a Redux Thunk action from within a TypeScript environment, beyond the confines of a React component

Currently, I am in the process of converting a React Native app into TypeScript. Unfortunately, I have encountered an issue with dispatching thunk actions outside of the store. Below is how my store is configured: store/index.ts import { createStore, app ...

Error: The selected module is not a valid top-level option

I am facing an issue while using the babel-loader. I have removed all irrelevant code and just kept the error-related portion. What could be causing this problem? module.exports = merge(baseWebpackConfig, { ... module: { rules: [ ...

What steps can I take to troubleshoot and repair my accordion feature within an Angular project?

As a newcomer to Angular, I recently attempted to create an accordion component but encountered unexpected behavior. Here is the HTML code for my attempt: <div class="faq-item-container"> <h1 class="mt-1 mb-5"><strong>Frequently A ...

The subscribe method in Angular TS may be marked as deprecated, but worry not as it is still

I have developed a function that retrieves new data from a service file each time it is called. Here is how the function looks: onCarChange() { this.carService.getCarData(this.selectedCar).subscribe( async (response: CarData) => { if (response?.d ...

This error occurred: "Property 'release' cannot be read because it is undefined."

Hello everyone! I'm in need of some assistance. I am trying to test my service tree with a specific structure. Here is an overview of my test: describe(`Service selector`, () => { describe(`getCurrentServiceTree`, () => { it(`should bui ...