Steps for assigning a parameter to a general purpose function

Having a generic function named retrieve:

function retrieve<T>(endpoint: string, id: string, /* etc */): T {}

The goal is to define a function like retrieveUser, which binds the first parameter and specifies T.

An attempt was made using Function.prototype.bind():

const retrieveUser = retrieve.bind(/* this */ undefined, "/user");

However, there was an issue with specifying T.

Another approach involved rest parameters:

type User = { username: string, /* etc */ };
const retrieveUser = (...args) => retrieve<User>("/user", ...args);

This method achieves the intended result but Typescript raised some complaints:

  • error TS7019: Rest parameter 'args' implicitly has an 'any[]' type.
  • error TS2556: A spread argument must either have a tuple type or be passed to a rest parameter.

The question arises whether it's possible for Typescript to infer types of args from the retrieve function. Is there a more efficient way to achieve this objective?

Answer №1

TypeScript doesn't natively support the specific type of higher-order generic function manipulation you're looking for in this scenario. It would be ideal to specify "use T in typeof retrieve with User", but unfortunately, there isn't a straightforward way to achieve this within the language. Perhaps if TypeScript included features like higher-kinded types or generic values, as requested in microsoft/TypeScript#1213 or microsoft/TypeScript#17574, this could be done more smoothly.


To work around this limitation without being ad-hoc, you can explicitly define the type "specify T in typeof retrieve with User", annotate a variable with that type and assign retrieve to it:

const userRetrieve: (endpoint: string, id: string, /* etc */) => User = retrieve; // okay

The compiler accepts this approach, indicating that although programmatically specifying T with

User</code in the type system may not be easy, it recognizes that <code>retrieve
is compatible with the intended type. This maintains type safety by flagging any errors if the type is incorrect.

Subsequently, you can bind a parameter to userRetrieve:

const retrieveUser = userRetrieve.bind(undefined, "/user");
// const retrieveUser: (id: string) => User

If the parameters of retrieve are non-generic, you can utilize the Parameters<T> utility type to streamline the process:

const userRetrieve: (...args: Parameters<typeof retrieve>) => User = retrieve; // okay
const retrieveUser = userRetrieve.bind(undefined, "/user");
// const retrieveUser: (id: string) => User

In situations where expedience outweighs strict type safety, you can replace the annotated variable with a type assertion:

const retrieveUser =
  (retrieve as (...args: Parameters<typeof retrieve>) => User).
    bind(undefined, "/user");

This concise method sacrifices some level of type safety for convenience.


If the parameters rely on the generic type parameter, using Parameters<T> will not retain track of any type parameter:

declare function retrieve<T>(endpoint: string, id: string, somethingGeneric: T): T;

const retrieveUser =
    (retrieve as (...args: Parameters<typeof retrieve>) => User).
        bind(undefined, "/user");
// const retrieveUser: (id: string, somethingGeneric: unknown) => User 

In this case, where the type is mistakenly inferred as unknown instead of

User</code, manual intervention is necessary to rectify this discrepancy:</p>
<pre><code>const retrieveUser =
    (retrieve as (endpoint: string, id: string, somethingGeneric: User) => User).
        bind(undefined, "/user");
// const retrieveUser: (id: string, somethingGeneric: User) => User

While there are ways to leverage limited support for higher order generic function inference to automate inserting T, it's quite complex and esoteric. For further insights into this method, refer to this answer.

class GenTypeMaker<T> {
    getGenType!: <A extends any[], R>(cb: (...a: A) => R) => () => (...a: A) => R;
    genType = this.getGenType(null! as typeof retrieve)<T>()
}
type Retrieve<T> = GenTypeMaker<T>['genType']
// type Retrieve<T> = (endpoint: string, id: string, somethingGeneric: T) => T

With careful specification, the type for Retrieve<T> aligns with the function type of retrieve</code when <code>T is designated. Thus, Retrieve<User> encompasses the desired type:

const retrieveUser =
    (retrieve as Retrieve<User>).
        bind(/*this*/ undefined, "/user");
// const retrieveUser: (id: string, somethingGeneric: User) => User

This technique is intriguing but not recommended for everyday use. In cases like these, it's advisable to manually declare and annotate the appropriate types for clarity and accuracy.

Playground link to code

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

Using Nestjs to inject providers into new instances of objects created using the "new" keyword

Is it possible to inject a provider into objects created by using the new keyword? For instance: @Injectable() export class SomeService { } export class SomeObject { @Inject() service: SomeService; } let obj = new SomeObject(); When I try this in my t ...

The Vue data retrieved from an API using onMounted() is not initially showing up in the DOM. However, it magically appears after I make changes to the template

Hello and thank you to those taking the time to read this. I am new to Vue, so I may be overlooking something obvious here, but after being stuck for several days, I am reaching out for help. In my SFC file, I have an onMounted function fetching data from ...

Simulating a PubSub publish functionality

I have been trying to follow the instructions provided in this guide on mocking new Function() with Jest to mock PubSub, but unfortunately I am facing some issues. jest.mock('@google-cloud/pubsub', () => jest.fn()) ... const topic = jest.fn( ...

How can one access a dynamically generated element in Angular without using querySelector?

Currently in the process of developing my custom toastr service, as shown in the GIF below My Objective: https://stackblitz.com/edit/angular-ivy-tgm4st?file=src/app/app.component.ts But without using queryselector. It's recommended to avoid querysele ...

Trouble arises when attempting to transfer cookies between server in Fastify and application in Svelte Kit

In the process of developing a web application, I am utilizing Fastify for the backend server and Svelte Kit for the frontend. My current challenge lies in sending cookies from the server to the client effectively. Despite configuring Fastify with the @fas ...

The typing library for Angular does not properly identify the JQueryStatic object

Encountered an issue with the Angular declaration file: Error TS2304: Cannot locate identifier 'JQueryStatic'. The typings for jQuery are installed and properly declare JQueryStatic as an interface. Looking for solutions to resolve this error. ...

Why did the compilation of Next.js using TypeScript and ESLint succeed despite encountering errors?

I've been delving into Next.js and encountered unexpected results when integrating TypeScript and ESLint. ESLint seems to work well with TypeScript, but my project compilation is successful despite encountering errors. It's puzzling why the comp ...

Master the art of properly switching on reducer-style payloads in Typescript

Currently, I am dealing with two types of data: GenArtWorkerMsg and VehicleWorkerMsg. Despite having a unique type property on the payload, my Searcher is unable to differentiate between these data-sets when passed in. How can I make it understand and dis ...

Typescript is failing to return nested types when attempting to return a nested object

My goal is for my function to return a nested type of Content, but it's not working even though the type that should be returned is known. Let's take a look at an example: type Content = { some: { extra: string; prop: number; ...

Calculate the total value of an object using Typescript

Here is the data that I have: [Products, Products, Products, Products] 0: Products {product_id: "1", Product_type_id: "11", Subtotal:450, …} 1: Products {product_id: "2", Product_type_id: "22", Subtotal:550, …} 2: Products {product_id: ...

Nest.js: initializing properties from a superclass in a controller

I have a question about unit testing controllers in the Nest.js framework. My issue is that the property from a superclass is not initialized in the controller class when creating a test module. Here is an example of the code I am referring to: export cl ...

How to display currency input in Angular 2

Is there a way to dynamically format input as USD currency while typing? The input should have 2 decimal places and populate from right to left. For example, if I type 54.60 it should display as $0.05 -> $0.54 -> $5.46 -> $54.60. I found this PLUN ...

What's the deal with the `return of ()` syntax?

Just came across this piece of code: https://i.sstatic.net/JZXP5.png Code snippet in typescript. The first line looks like: ... return of (true); Can someone explain this syntax to me? ...

The type '{ children: ReactNode; }' does not share any properties with the type 'IntrinsicAtrributes'

I have explored several discussions on the topic but none of them have provided a solution to my issue. My objective is to develop a reusable Typography component that resembles the following structure: import React from 'react' import type { Ty ...

The error message "Unable to find 'encoding'" in NextJS is triggered by the use of try/require in the node_modules folder

Running a NextJS app in typescript with version 13.4.19, utilizing @apollo/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="0d7e687f7b687f4d392334233e">[email protected]</a> triggers a warning during the build proce ...

Access-Control-Allow-Origin header not being sent by ExpressJS

In the midst of my project, I find myself needing an angular web application to connect with a node/express backend. Despite trying to implement Cors for this purpose, the express server refuses to send the Access-Control-Allow-Origin header. I am perplexe ...

Selecting ion-tabs causes the margin-top of scroll-content to be destroyed

Check out the Stackblitz Demo I'm encountering a major issue with the Navigation of Tabs. On my main page (without Tabs), there are simple buttons that pass different navparams to pre-select a specific tab. If you take a look at the demo and click t ...

Interactive Angular Interfaces Featuring Numerous Numeric Choices Within the Main Object

I am currently in the process of designing an interface for my Angular project that allows users to search for video games using the WhatToPlay API within RapidAPI. When a user searches for details on a video game, the result will return a game identified ...

Tips for extracting a keyword or parameters from a URL

I'm in the process of creating my personal website and I am interested in extracting keywords or parameters from the URL. As an illustration, if I were to search for "Nike" on my website, the URL would transform into http://localhost:3000/searched/Nik ...

In Typescript, we can streamline this code by assigning a default value of `true` to `this.active` if `data.active

I am curious if there is a better way to write the statement mentioned in the title. Could it be improved with this.active = data.active || true? ...