In the CallableFunction.call method, the keyword "extends keyof" is transformed into "never"

In the following method, the type of the second parameter (loadingName) is determined by the key of the first parameter.

(alias) function withLoading<T, P extends keyof T>(this: T, loadingName: P, before: () => Promise<any>): Promise<void>
import withLoading

However, when invoking withLoading.call, the type of loadingName unexpectedly changes to never:

(method) CallableFunction.call<this, [loadingName: never, before: () => Promise<any>], Promise<void>>(this: (this: this, loadingName: never, before: () => Promise<any>) => Promise<...>, thisArg: this, loadingName: never, before: () => Promise<any>): Promise<...>

Answer №1

If we aim to simplify this into a minimal reproducible example, consider the scenario where you have the function func defined as follows:

declare function func<T>(this: T, key: keyof T): void;

You attempt to call it like this, but encounter an error:

func.call({ a: 123 }, "a") // error?!
// CallableFunction.call<{ a: number; }, [key: never], void>(...);

Why does this error occur?


The issue arises when trying to combine two generic functions with the expectation that the compiler will infer a relationship between them to pass on generic type parameters without explicit specification.

Although TypeScript 3.4 introduced support for some aspects of higher order type inference from generic functions, and TypeScript 3.5 extended this to include generic constructors, it falls short in cases like this due to heuristic limitations.

Had there been a stand-alone function call() without a this parameter, the desired behavior would work:

declare function call<T, A extends any[], R>(
  thisArg: T, cb: (this: T, ...args: A) => R, ...args: A): R;
call({ a: 123 }, func, "a") // okay
call({ a: 123 }, func, "b") // error

But why doesn't it work as expected with a this parameter?


The reason lies in the fact that current support for higher order function inference is heuristic and not comprehensive. The existing algorithm, while imperfect, serves performance goals, as highlighted in microsoft/TypeScript#30215.

The above algorithm is not a complete unification algorithm and it is by no means perfect.

Achieving full unification, as discussed in microsoft/TypeScript#30134, would necessitate substantial changes to TypeScript's type inference mechanism and might impact compiler performance. For now, the algorithm remains imperfect yet efficient.


While investigating, I came across microsoft/TypeScript#33139, a pull request promising improved inference with this parameters. Despite being under the lead language architect's purview, its status has remained stagnant for some time without associated bug reports or feature requests.


To address this, an immediate workaround involves manually specifying the generic type parameters:

func.call<{ a: number }, [key: keyof { a: number }], void>({ a: 123 }, "a"); // okay

Although it's not ideal, at least such manual specifications are recognized by the compiler for func.call, eliminating the need for unsafe type assertions.

In the long run, providing feedback on microsoft/TypeScript#30215 regarding the benefits of a full unification algorithm could be beneficial. Additionally, advocating for extending TypeScript 3.4's support for higher-order functions to cover this parameters through a separate feature request linked to microsoft/TypeScript#33139 might prompt consideration, though outcomes aren't guaranteed.

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

Reducing the amount of text displayed on ion-text to a minimum

HTML: <ion-list *ngFor="let message of messages"> <ion-item lines="none" type="button" button="true"> <ion-grid> <ion-row> <ion-col class="message"> <ion-text> ...

Tips for preventing circular dependencies when using combineSlices in Redux-toolkit

When utilizing combineSlices with createAsyncThunk condition, I find it challenging to avoid circular dependency. My store initiation thunk looks like this: thunk.ts export const initiateFx = createAsyncThunk< InitiatePayload, string, { state: R ...

Choosing Vue select options depending on a condition

I am working on a dropdown template with Vue.js and have encountered some challenges. Here is the basic structure of my dropdown: <select v-model="selectedClient" class="stat-select text-u-c"> <option disabled value="">Please select a Client ...

Injecting services and retrieving data in Angular applications

As a newcomer to Angular, I am trying to ensure that I am following best practices in my project. Here is the scenario: Employee service: responsible for all backend calls (getEmployees, getEmployee(id), saveEmployee(employee)) Employees components: displ ...

Utilizing ReactJS and TypeScript to retrieve a random value from an array

I have created a project similar to a "ToDo" list, but instead of tasks, it's a list of names. I can input a name and add it to the array, as well as delete each item. Now, I want to implement a button that randomly selects one of the names in the ar ...

Issue with TypeScript not detecting exported Firebase Cloud Functions

Dealing With Firebase Cloud Functions Organization I am managing a large number of Firebase Cloud Functions, and in order to keep the code well-structured, I have divided them into separate files for each function category (such as userFunctions, adminFun ...

There is no overload that fits the current call | Typescript, React, and Material UI

Embarking on my TypeScript journey with React and Material UI, I am hitting a roadblock with my initial component. // material import { Box } from '@material-ui/core'; // ---------------------------------------------------------------------- ...

Integrate Angular 2 components into WebStorm

I am currently working on a project using Angular 2 (rc5) and TypeScript (1.8.10). Angular 2 is built with TypeScript, but in the node_modules directory, I notice that there are JavaScript files (*.js) along with declaration files (*.d.ts). It makes it di ...

Can someone help me figure out how to simulate an express middleware class method using jest and supertest?

I'm facing some challenges trying to achieve the desired outcome when mocking a method in a class using jest and supertest. I'm specifically looking for a solution that can help me bypass the verifyAuthenticated method with a mocked version in or ...

Can you tell me the data type of a Babel plugin parameter specified in TypeScript?

Struggling to find ample examples or documentation on writing a Babel plugin in TypeScript. Currently, I am working on a visitor plugin with the following signature: export default function myPlugin({ types: t }: typeof babel): PluginObj { In order to obt ...

Angular 6 - ngModel Value Reveals Itself upon User Interaction

I am currently working on a component that lists items with a dropdown option to change values. However, I have noticed a small issue where the selected item in the dropdown appears empty upon component creation. The selection only becomes visible after cl ...

Description: TypeScript type that derives from the third constructor parameter of a generic function

How can I determine the type of constructor props for a generic type? Take a look at this example. type PatchableProps<T> = T extends { [k: string | number]: any } ? { [Key in keyof T]: PatchableProps<T[Key]> } : T | Patch export class ...

Personalized path-finding tree iterator

I am trying to implement a custom iterator in JavaScript that can traverse a DOM tree based on specific criteria provided by a callback function. The goal is to return an array of the nodes that match the criteria as the generator iterates through the tree ...

How can I dynamically update content using <router-outlet> on a secondary page?

When accessing my homepage, I want to see a header, footer, and the home-news-page displayed. Additionally, when I click on a link in the header, I would like the content of the home-news-page to change accordingly. Here is how my routing is currently set ...

Achieving TypeScript strictNullChecks compatibility with vanilla JavaScript functions that return undefined

In JavaScript, when an error occurs idiomatic JS code returns undefined. I converted this code to TypeScript and encountered a problem. function multiply(foo: number | undefined){ if (typeof foo !== "number"){ return; }; return 5 * foo; } ...

What causes the difference in behavior between packed and non-packed generics?

When attempting to exclude properties outside of generics, it functions properly but results in a breakdown within the generic context. The issue lies in the fact that Omit<Thing, 'key1' | 'key2'> transforms into Omit<Thing, &a ...

Tips for effectively narrowing the `undefined` type

Why am I getting this error message? const func = (a: unknown) => { if (a && typeof a === 'object' && 'b' in a) { a.b; } }; The error message I'm receiving is: Property 'b' does not exist on ty ...

Sharing a variable between an Angular component and a service

I am attempting to pass a variable from a method to a service. from calibration-detail.component.ts private heroID: number; getTheHeroID() { this.heroService.getHero(this.hero.id).subscribe(data =>(this.heroID = data.id)); } to step.service.ts I ...

Tips for executing numerous asynchronous tasks in Ionic 3 and closing a loader once all tasks are completed

Currently, I am in the process of developing an Ionic 3 application that offers the feature to cache a list of articles content on demand. The implementation involves utilizing Storage which employs promises for its operations. The code snippet I have wri ...

Even though there is data stored in the array, the React Native array.length appears to be returning a value

I am struggling with what appears to be a simple issue, and it's frustrating that I've had to seek help for this. The problem lies in iterating through an array messages: Message[] = [...]. No matter what method of iteration I try, it doesn&apos ...