Typescript's Versatile Promise: Exploring Polymorphism

I am attempting to parameterize a function within a Typescript class that returns a Promise. After the completion of the promise, I return this, which is then used polymorphically by the caller. However, I am encountering a compile-time error that I am struggling to comprehend.

The following simplified code compiles without any issues:

class foo {
  aFoo(): Promise<foo> {
    return new Promise<foo>(resolve => resolve(this));
  }
}
class bar extends foo {
  test() {
    this.aFoo().then(result => {
      let val: bar;
      val = result as bar;
    });
  }
}

Despite this working solution, I aim to avoid having to downcast results, such as with val = result as bar, every time I call this function. Hence, my attempt to parameterize the function in the superclass:

class foo {
  aFoo<T extends foo>(): Promise<T> {
    return new Promise<T>(resolve => resolve(this));
  }
}
class bar extends foo {
  test() {
    this.aFoo<bar>().then(result => {
      let val: bar;
      val = result;
    });
  }
}

However, I am facing a compiler error on resolve(this) in the promise returned from aFoo.

The error message states:

this: this
Argument of type 'this' is not assignable to parameter of type 'T | PromiseLike<T> | undefined'.
  Type 'foo' is not assignable to type 'T | PromiseLike<T> | undefined'.
    Type 'foo' is not assignable to type 'PromiseLike<T>'.
      Type 'this' is not assignable to type 'PromiseLike<T>'.
        Property 'then' is missing in type 'foo' but required in type 'PromiseLike<T>'.ts(2345)
lib.es5.d.ts(1393, 5): 'then' is declared here.

To suppress the compiler error, I can utilize some redundant casting:

return new Promise<foo>(resolve => resolve((this as unknown) as T));

While using this workaround does solve the issue, I would like to gain an understanding of why the compiler is raising concerns. Initially, I thought it might be connected to the complexities of this in JS/TS, yet even converting this into an arrow function did not eliminate the error. Additionally, the error seems peculiar to me, as it describes this as a type rather than an instance - although I acknowledge that this can indeed be utilized within a type context in TS. Any insights into where I may have erred?

Answer №1

TypeScript introduces the concept of polymorphic this type, as demonstrated here.

By using this as a type, you can specify that something has the Promise<this> type, and it functions as intended:

class foo {
  aFoo(): Promise<this> {
    return new Promise<this>(resolve => resolve(this));
  }
}

class bar extends foo {
  test() {
    this.aFoo().then(result => {
      let val: bar;
      val = result; // This assignment is valid because 'result' has type 'bar'
    });
  }
}

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

Transform a collection of objects into instances of a class

I have a scenario where I am fetching an array of objects from PHP and my goal is to transform this data into instances of a class called ContentData. The current solution that I have seems to work fine, but deep down I sense that there might be a more el ...

"Transforming a Java byte array into a ReactJS video: Step-by-step guide

I am getting a file from a Java API server and need to convert it into a video using React JS. The Java API server converts the video file into a byte array using Files.readAllBytes(file) and I need to use this video file byte array to create a video fil ...

What was the reason for the delay in updating the local state within the ToggleButtonsGroup MUI component caused by the onChange handler?

I've encountered an issue with the ToggleButtonGroup component from the material-ui library, specifically in the onChange handler. I started with a more complex code and simplified it step by step to arrive at this code snippet. However, I'm puzz ...

Angular 2 orderByPipe not displaying any results initially

At first, I thought the reason for not displaying anything initially was due to it not being async and returning an empty array. Everything worked fine without the pipe, but the posts weren't shown on startup. After submitting a post, all the posts ap ...

Mongoose: Executing multiple find() queries within a forEach loop - Searching for the final then() promise

I am struggling with using multi find() to populate 'categories' and 'pic' for each 'post'. The issue I am facing is that I cannot determine where the data is being returned in full before sending 'posts' using &apos ...

Send two unique GUID parameters to an API using Angular

I have created an API for following a user. This method requires two parameters, which are both GUIDs. Here is the code snippet: // Follow user [HttpPost] public async Task<ActionResult<Guid>> FollowUser([FromBody] Guid user_gd, Guid user2_gd) ...

Testing AG Grid's TypeScript in-line cell editing using Jest

I am currently working on writing Jest tests to evaluate the functionality of my ag-grid table. So far, I have created tests to check the default data in the grid and to test a button that adds an additional row of data to the grid. I am now attempting t ...

Calculating the number of days between two dates with angular

I need assistance with my application. I have an API response that includes a message sent date, and I am looking to calculate the difference in days between the current date and the date from the API response using Angular 8 along with map in ngFor. Here ...

Angular 4: Leveraging a directive as a universal constant

I am looking to develop a directive that allows me to utilize a template variable in order to access a global variable, much like $rootScope in Angular.JS. The goal is to avoid having to inject a service into every component where I need access to the vari ...

What sets apart the usage of 'export declare function' from 'export function' within a '.d.ts' file?

While reviewing typescript declaration files, I noticed that some people write declarations like this: export function useTheme(): ITheme; I thought that the declare keyword was needed when declaring types for functions defined elsewhere. Is it valid to ...

Unable to locate the values for the name provided

I have been attempting to execute a sample code written in TypeScript (version 2.6) that uses async iterator within the browser. ` function* countAppleSales () { var saleList = [3, 7, 5]; for (var i = 0; i < saleList.length; i++) { yield saleL ...

Problem encountered when using the Mongoose find() method with the exec() function

Could you please clarify why the code snippet below is returning audiences instead of an empty array? return Audience.find() .exec((err, audiences) => { if (err) return errorHandler.handle('audienceService', err); return Promise.re ...

The method for retrieving a generic type property within a dynamic interface

Is there a way to access a dynamic T property within an interface in order to enhance its typing for generic functions like this: type AnotherType<T extends {}> = T & { prop: boolean; prop2: string; }; interface SpecialInterface<T> ...

Ways to expand the inheritance of a family's assets in the next generation

I am facing an issue with the following code snippet: class Table { get pagination () { return { get item () { return { log (s : string) { console.log(s); ...

Exporting an Excel file from JSON with customized formatting and automatic cell adjustment using FileSaver.js

I have successfully implemented functions to export JSON data to Excel using the code below: public exportAsExcelFile(json: any[], excelFileName: string) :Promise<Object> { const worksheet: XLSX.WorkSheet = XLSX.utils.json_to_sheet(json); ...

Angular's strict mode causing issues with CustomValidator for email field in form

Trying to correctly define an email form field: this.form.addControl('email', new FormControl('', [ Validators.required, CustomFormValidators.isValidEmail ])); Utilizing a CustomFormValidators class that includes an isValidEmail me ...

The power of Vue reactivity in action with Typescript classes

Currently, I am working on a Vue application that is using Vue 2.6.10 along with Typescript 3.6.3. In my project, I have defined a Typescript class which contains some standard functions for the application. There is also a plugin in place that assigns an ...

I am interested in utilizing the request-reply pattern with KafkaJS in a TypeScript environment. Could you provide guidance on how to successfully implement this?

I am new to Kafka and I am trying to implement the request-reply pattern using kafkajs in TypeScript. However, my current implementation is very basic and the consumers inside producers are taking too long to start, causing delays. Is there a better way to ...

Tips for testing a mapbox popup using jasmine testing?

I encountered an issue with my mapbox popup while using jasmine and attempting to write a unit test for it. Here is the function in question: selectCluster(event: MouseEvent, feature: any) { event.stopPropagation(); this.selectedCluster = {geo ...

Updating Key-Value pairs in an ArrayList using Angular 4

After importing json data into an arrayList and storing it in local-storage, the structure looks like this: [ { "id": 1, "name": "Albany", "manufacture": "Albany Superior Low Gi Sliced Brown Seed Bread 700g", "price": 1 ...