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

Navigating Through Angular Components

I have a layout with 3 components arranged like this: <app-header></app-header> <app-body></app-body> <app-footer></app-footer> However, I want to change the positioning of the footer and body as shown below: <app-he ...

Animating Angular ng-template on open/close state status

I am looking to add animation when the status of my ng-template changes, but I am unable to find any information about this component... This is the code in my report.component.html <ngb-accordion (click)="arrowRotation(i)" (panelChange)="isOpen($even ...

Storing application state using rxjs observables in an Angular application

I'm looking to implement user status storage in an Angular service. Here is the code snippet I currently have: import { Injectable } from '@angular/core'; import { BehaviorSubject } from 'rxjs/BehaviorSubject'; @Injectable() expo ...

Using Svelte with Vite: Unable to import the Writable<T> interface for writable store in TypeScript

Within a Svelte project that was scaffolded using Vite, I am attempting to create a Svelte store in Typescript. However, I am encountering difficulties when trying to import the Writable<T> interface as shown in the code snippet below: import { Writa ...

Using the parameter value as a property name in the return type of a function in TypeScript: a guide

After creating a function that converts an object to an array where each element contains the ID of the object, I encountered a new requirement. The current function works great with the following code: const objectToArray = <T>(object: { [id: string ...

Dealing with the endless looping problem in Next.js caused by useEffect

Looking to implement a preloader that spins while the content is loading and disappears once the loading is complete. However, encountering an issue where the site gets stuck on the loading page and keeps loading infinitely. I've tried multiple soluti ...

Where's the tsconfig.json for Firebase Emulators?

I've encountered an issue with my Firebase project that's written in JavaScript (not TypeScript). When attempting to run the functions emulator, I'm getting the following error: $ firebase emulators:start --only functions ⚠ functions: Ca ...

Yarn Plug'n'Play was unable to locate the module during the next build

Currently, I am in the process of developing a Next.js project with yarn's Plug'n'Play feature. In this project, I have created several pages and added various packages, including mathjs: '^10.3.0' to assist me in parsing user inpu ...

What is the solution to the error message "Unable to assign property of undefined"?

I am currently working on an angular countdown timer and encountering a TypeError when attempting to access a variable from another component. I am struggling to identify the root cause of this issue. Here is the video tutorial that I am using as a referen ...

Create a global variable by importing an external module in TypeScript

I am currently developing a TypeScript npm module called https://www.npmjs.com/package/html2commonmark. This module is versatile and can be utilized in nodejs (via require) as well as in the browser (by loading node_modules/html2commonmark/dist/client/bund ...

Error in Express Session: Property 'signin' is not found in type 'Session & Partial<SessionData>'. Code: 2339

I received the following Error Code: Property 'signin' does not exist on type 'Session & Partial<SessionData>'. (2339) About My Application src/index.ts import "reflect-metadata"; import express = require("expr ...

Troubleshooting: Vue.js component events not being detected in TypeScript

I'm encountering an issue with receiving events from a barcode reader, which I heavily referenced from a GitHub repository. The problem lies in the fact that the events emitted by the BarcodeScanner component are not being captured by the enclosing c ...

What is the best way to interpret a line break within a string variable in TypeScript?

Realtime Data base contains data with \n to indicate a new paragraph. However, when this data is retrieved and stored in a String variable, the website fails to interpret the \n as a paragraph break: https://i.stack.imgur.com/tKcjf.png This is ...

Javascript Library Issue: "Implicitly Declared Type 'Any' Error"

I am currently in the process of developing a JavaScript library that will interact with an API. My goal is to create a module that can be easily published on npm and utilized across various frameworks such as Angular or React. Below is the code snippet fo ...

Struggling to make Cypress react unit testing run smoothly in a ReactBoilerplate repository

I have been struggling for the past 5 hours, trying to figure out how to make everything work. I even recreated a project's structure and dependencies and turned it into a public repository in hopes of receiving some assistance. It seems like there mi ...

Is there a way to dynamically retrieve a JSON element in Typescript?

I am using a data grid throughout my application, and currently, I am retrieving the selected rowid with the following code. Here is the HTML snippet: <tbody> <tr *ngFor="let ddata of tableData.data; let i = index" (click)="setClickedRow(ddat ...

Tips for managing numerous HTTP requests in Angular 6

I have a method that is trying to chain together 3 requests like this: showProfileDetails() { this.getUserInfo(this.currentUser.id).pipe( mergeMap(e => this.getAccounts(this.currentUser.id) ), mergeMap(e => this.getPayments ...

Exploring Typescript keyof in Storybook's User Interface customizations

Currently, I am working on developing components for integration with Storybook, but I am encountering an issue related to Typescript inferred types. While striving for code reusability, I prefer not to specify the options for a control within the story i ...

Navigating through the concept of passing objects by reference in child components of Angular 2+

Understanding that TypeScript uses object passing by reference can be challenging, especially when dealing with deep copy issues. This becomes particularly cumbersome when working within a theme. I recently encountered an issue with a component containing ...

Angular is used to call a function that captures a specific div and then waits for the capture to be completed before

I'm facing a challenge where I need to handle the capturing of a div using a method called capture() within another method. Take a look at the code snippet below: theimage; // declaring the variable callcapture() { // perform certain actions t ...