Error in Typescript with a Bluebird library: The 'this' context is not compatible with the method's 'this' context

I have a collection of methods (some synchronous and some asynchronous) that I wish to process sequentially using bluebird.each. Here's a simplified example:

import bluebird from 'bluebird';

const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
class Template {
  propertyA!: number;
  propertyB!: number;
  constructor() {}
  async methodA() {
    this.propertyA = 10;
    console.log({ thisArgInA: this, propertyA: this.propertyA });
    await delay(500);
    return this.propertyA;
  }
  methodB() {
    this.propertyB = this.propertyA * 100;
    console.log({ thisArgInB: this, propertyB: this.propertyB });
    return this.propertyB;
  }
}

const instance = new Template();

const sequence = [instance.methodA, instance.methodB];

(async function main() {
  await bluebird.each(sequence, (fn) => {
    const result = fn.call(instance);
    console.log({ result });
  });
})();

An error is being thrown which I find puzzling:

index.ts:27:20 - error TS2684: The 'this' context of type '(() => Promise<number>) | (() => number)' is not assignable to method's 'this' of type '(this: Template) => Promise<number>'.
  Type '() => number' is not assignable to type '(this: Template) => Promise<number>'.
    Type 'number' is not assignable to type 'Promise<number>'.

27     const result = fn.call(instance);

I speculated whether the issue arose because bluebird.each accepts either values or promises that resolve to values causing confusion for the compiler. My attempt at resolving this was to enforce strong typing on the return values"

async methodA: Promise<number> { /*...*/ }
methodB: number { /*...*/ }

However, this did not yield any changes. Additionally, observations include:

  • The need for explicit context since executing the functions within the each callbacks severs their connection with the Template object as this.
  • When methodA operates synchronously (removing the async keyword, eliminating the delay(), and directly returning this.propertyA), all is well.
  • Removing the return statements also resolves issues.
  • Switching from fn.call(instance) to fn.bind(instance)() restores functionality.

How can I conform to the compiler's expectations so it correctly handles calling these functions in the specified context?

Is there a simpler way to execute these methods sequentially while preserving their link to this?

StackBlitz Example

Answer №1

Considering the inferred types can be beneficial:

  • sequence:
    (((this: Template) => number) | ((this: Template) => Promise<number>))[]
    or simply
    ((() => Promise<number>) | (() => number))[]
  • fn:
    ((this: Template) => number) | ((this: Template) => Promise<number>)
    or just
    (() => Promise<number>) | (() => number)
  • .call:
    <Template, [], number>(this: (this: Template) => number, thisArg: Template): number

It is evident that the type of the .call() expression is not as expected - the compiler requires a function that returns a number, which causes issues because fn might return a Promise<number> instead. This discrepancy could stem from a TypeScript glitch when inferring the return type of function union types. It seems to be linked to the subtle contrast between X => A | B and (X => A) | (X => B).

To rectify this, we can do the following:

  • const result = fn.call<Template,[],number|Promise<number>>(instance);
    or
  • …(fn: () => number | Promise<number; )) => …
    or
  • const sequence:
    (() => number | Promise<number>)[]

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

React State not refreshing

Currently tackling a challenging e-commerce project and facing an obstacle with the following component: import React, { useEffect, useState } from 'react'; const Cart = () => { let [carts, setCarts] = useState([]); let [price, se ...

Saving vast array in MongoDB

I am currently working on a personal project that involves implementing a search feature. My setup includes using typeahead.js with a REST api created in expressJS and mongoDB. I could use some guidance on two particular challenges I am facing. While my ba ...

Issue with template updating when utilizing ui-router and ion-tabs in Ionic framework

CODE http://codepen.io/hawkphil/pen/LEBNVB I have set up two pages (link1 and link2) accessible from a side menu. Each page contains 2 tabs: link1: tab 1.1 and tab 1.2 link2: tab 2.1 and tab 2.2 To organize the tabs, I am utilizing ion-tabs for each p ...

Encountering a Typescript issue while utilizing day classes from Mui pickers

Recently, I encountered an issue with my code that alters the selected day on a Mui datepicker. I came across a helpful solution in this discussion thread: MUI - Change specific day color in DatePicker. Although the solution worked perfectly before, afte ...

The display function in Javascript has a tendency to work sporadically

I’ve been tasked with creating my own tic tac toe game through coding. While I am relatively new to programming, I have a strong passion for it. At the moment, I've set up a basic function to hide only the "O", leaving only the "X" visible on the gr ...

What methods does TypeScript use to verify primitive types during runtime?

Is it true that Typescript removes any type or interface at compile time? If so, how is it possible to determine an object's primitive type using the typeof keyword? Are custom interfaces handled differently than primitive types? const a = "strin ...

What techniques can I implement to optimize the speed of this feature in JavaScript?

I have developed a feature that highlights any text within a <p> tag in red based on a user-specified keyword. The current implementation works well, but it is slow when dealing with over 1000 lines of <p>. Is there a faster way to achieve this ...

Expect a promise to be resolved in the RootCtrl of Angular using $http

One of the functions in my RootCtrl is responsible for calling an http api and returning the result. $scope.checkAccess = function(){ var result = MyService.me(); result.then(function(response){ console.log(response); if (response. ...

When the mouse is clicked, rotate the object along its axis using OBJ Loader in THREE.js

I am looking to add a feature where my object rotates on its axis when the mouse is dragged. The challenge I am facing is that I can only access my skull object within the function, which limits where I can place a rotation increment inside render(). Coul ...

Exploring the search capabilities of input fields that are disabled

Lately, I've been struggling with a search function on my table of information. The trouble started when I made the text in the table editable - now the search function refuses to cooperate. I've tried troubleshooting it from different angles, b ...

A comprehensive guide on using ajax to reproduce a Postman POST API request

Currently, I am able to retrieve an "access_token" using Postman; however, I am attempting to recreate this process in ajax for experimentation purposes on jsfiddle. In Postman, the following setup is used: A POST request URL: No active headers Body in ...

PHP function json_encode() is causing an issue by returning an "Undefined" value

I'm working on a PHP project where I am creating an array and using json_encode() to convert it into JSON before retrieving it with $.getJSON. However, I am encountering an issue where it returns Undefined when trying to send all the data at once. Int ...

Executing a get request in Backbone without using the Option parameter by implementing the beforeSend method

After gathering insights from the responses to this and this queries, I have formulated the code below for a GET request: var setHeader = function (xhr) { xhr.setRequestHeader("Authorization", "Basic " + btoa($rootScope.login.Gebruikersnaam + ":" + $r ...

Creating a React component with a reference using TypeScript

Let's discuss a scenario with a reference: someReference; The someReference is essentially a React component structured like this: class SomeComponent<IProps> { getData = () => {}; render() { ...some content } } Now, how c ...

Update the react component's state props to trigger a re-render when the state changes

I'm facing an issue with getting a component to rerender in React even though one of its components is a piece of state that receives updates. The piece of state in question is 'board': const [board, setBoard] = useState("") The component i ...

The specified 'detail' property cannot be found on the given type '{}'. Error code: 2339

I encountered the error mentioned in the title while working on the code below. Any suggestions on how to resolve this issue? Any assistance would be greatly appreciated! import { useHistory } from "react-router-dom"; let h ...

Store the encoded data in a variable

After looking at some examples, I attempted to create my own solution but encountered an issue with the Promise being stuck in a "pending" state. My goal is to store base 64 data into a variable named base64. Can anyone offer guidance on what's wrong ...

Focusing on a text field after reloading a different div with AJAX

I've been spending a lot of time figuring out the following issue and I'm hoping someone can help me find the solution. My web application has an input field (type="text") that is ready to accept user input when the page loads. When the user nav ...

Storing a Promise in a Variable in React

As a newcomer to JavaScript/React, I am finding it challenging to grasp the concept of using Promise and async. To illustrate, consider the API call getSimById in a JS file that returns a Promise: export function getSimById(simId) { return fetch(simsUrl ...

Struggling to locate form elements within an HTML document? Explore the world of web scraping using Python and Selenium

I'm attempting to scrape this specific website, but it presents certain forms that need to be completed. My primary goal is to fill out these 5 forms (one appears after selecting another) and extract the data by clicking the "Consultar" button. Thes ...