What sets apart the Promise type returned by a regular function from that returned by an async function?

Imagine a scenario where I am working on a Typescript project that includes the latest packages:

In this project, I define a function that returns a Promise based on Typescript's native ambient declarations:

import * as q from "q";

function doSomethingElseAsync(): Promise<number> {
    return q.Promise<number>((resolve, reject) => {
        setTimeout(() => resolve(1), 5000);
    });
}

During compilation, an error is thrown by Typescript stating:

error TS2322: Type 'Q.Promise<number>' is not assignable to type 'Promise<number>'.
  Types of property 'then' are incompatible.
    Type '<U>(onFulfill?: ((value: number) => IWhenable<U>) | undefined, onReject?: ((error: any) => IWhena...' is not assignable to type '<TResult1 = number, TResult2 = never>(onfulfilled?: ((value: number) => TResult1 | PromiseLike<TR...'.
      Types of parameters 'onFulfill' and 'onfulfilled' are incompatible.
        Type '((value: number) => TResult1 | PromiseLike<TResult1>) | null | undefined' is not assignable to type '((value: number) => IWhenable<TResult1 | TResult2>) | undefined'.
          Type 'null' is not assignable to type '((value: number) => IWhenable<TResult1 | TResult2>) | undefined'.

Initially thinking it was due to Q Promises not being compatible with Typescript's declarations, the error disappeared completely when adding the async keyword to the function definition.

This peculiar behavior raises questions about whether this is a bug in Typescript, Q, or the Q typings, or if it is an expected but intricate feature of the compiler.

Answer №1

By adding the async keyword to your function in Javascript, it wraps the return value of that function in a native Promise. This way, whether the function returns a promise or a simple value, the result will always be a promise that can be easily awaited.

For instance, the implementation might look something like this:

Promise.resolve().then(() => doSomethingElseAsync())

If you don't use the async keyword and instead return a q promise, which is not a native instance of a Promise, you'll encounter a type error.

You can have a working solution without using q by making some modifications, as shown below:

function doSomethingElseAsync(): Promise<number> {
    return Promise<number>((resolve, reject) => {
        setTimeout(() => resolve(1), 5000);
    });
}

In case your environment lacks support for the native Promise class, changing the return type could also help, as illustrated here:

function doSomethingElseAsync(): q.Promise<number> {
    return q.Promise<number>((resolve, reject) => {
        setTimeout(() => resolve(1), 5000);
    });
}

Answer №2

Upon further investigation, it appears that the issue lies in the compatibility between Q promises and async/await.

My attempt to resolve this involved changing the return type of the async function from the native Promise to q.Promise. This adjustment led to an error being thrown by the compiler:

The type '<T>(resolver: (resolve: (val?: T | PromiseLike<T> | undefined) => void, reject: (reason?: any) =>...' is not suitable as an async function return type in ES5/ES3 due to its lack of association with a Promise-compatible constructor value.
  The type '<T>(resolver: (resolve: (val?: T | PromiseLike<T> | undefined) => void, reject: (reason?: any) =>...' does not align with the signature 'new <T>(executor: (resolve: (value?: T | PromiseLike<T> | undefined) => void, reject: (reason?: any) => void) => void): PromiseLike<T>'.

The error message highlights that the return type "is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value." This discrepancy can be attributed to the fact that q.Promise does not behave like a traditional constructor function callable with new. Conversely, alternative promise libraries such as Bluebird offer compatible constructor functions.

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

Unlocking the Powers of Angular JS: Mastering the Art of Binding to

I'm attempting to directly bind a promise to a view. Any suggestions on what I might be doing incorrectly? Disclaimer: The example code includes timeouts and static data for easier troubleshooting. UPDATE: JSFiddle Link: http://jsfiddle.net/YQwaf/27 ...

Unable to locate the type definition file for 'jquery'

After updating my NuGet packages, I encountered an issue where I can no longer compile due to an error related to the bootstrap definition file not being able to find the jquery definition file within my project. Prior to the update, the directory structu ...

Angular does not support the functionality of having multiple material paginations within a single component

I am currently working on creating a component that contains two dataTables, each with a different dataSource. However, I am facing an issue where my Tables are not visible immediately after the component is loaded due to the *ngIf directive being used. Be ...

Encountering a type mismatch error in Typescript while working with Redux state in a store file

It appears that I have correctly identified all the types, but could there be a different type of Reducer missing? 'IinitialAssetsState' is not assignable to type 'Reducer' The complete error message: Type '(state: { assets: n ...

retrieve a shared string from an array when toggled

Regarding the use of SelectionModel for mat-checkbox, a function is called on each click: toggleSelection(row) { this.selection.toggle(row); console.log("Selection"); console.log("this", this.selection.selected); this.selection.selected.f ...

Dealing with unresolved promises in async/await functions

What is the best approach for handling unresolved promises? For instance: class Utils { static async thisFunctionOnlyResolvesWhenPassed2AndNeverRejects(number: number) { return new Promise((resolve, reject) => { if(number === 2 ...

Updating a null value within the database was done successfully

Currently, I am working with angular CLI version 8.1.0 and have a user list displayed on a mat table. Upon clicking on a specific user, a new page opens up containing two buttons - "approve" and "reject". The issue I am facing is that when I click on "ap ...

Chart.js Axis Labels Orientation Guidelines

I am currently utilizing chart.js within an Angular 8 environment using Primeng. I am looking to customize the options for my chart as follows: For the y-axis ticks, set textDirection to 'ltr' For the x-axis ticks, set textDirection to 'rtl ...

Deriving values in Typescript based on a subset of a union for conditional typing

Can someone provide assistance with type inference in TypeScript to narrow a union based on a conditional type? Our API validates a set of parameters by normalizing all values for easier processing. One parameter can be either an array of strings or an ar ...

Utilizing class-validator for conditional validation failure

Implementing conditional validation in the class-validator library using the given example, I need to ensure that validation fails if the woodScrews property is assigned a value when the tool property is set to Tool.TapeMeasure. I've searched extensiv ...

Selecting the best approach to use based on the argument at hand

The issue at hand I am facing a challenge with a method foo(msg: string, arg: string). This method calls another method from the object bar, located within the same class as foo. The specific method to be called is determined by the value of arg. I am loo ...

Efficiently process and handle the responses from Promise.all for every API call, then save the retrieved data

Currently, I am passing three API calls to Promise.all. Each API call requires a separate error handler and data storage in its own corresponding object. If I pass test4 to Promise.all, how can I automatically generate its own error and store the data in ...

Enhancing your TypeScript version

Whenever I try to update my global version of TypeScript by running npm install -g typescript, the Angular project still shows an older version when I run ng v. Why does this happen and how can I ensure a consistent upgrade? https://i.stack.imgur.com/JzwK ...

Asynchronously parsing CSV files in NodeJs using the `await` keyword within the `on('data')`

I have a specific code snippet that is designed to read lines from a .csv file one by one and then store each processed row into a database const csv = require('csv-parse') const errors = [] csv.parse(content, {}) .on('data', async ...

JavaScript's async function has the capability to halt execution on its own accord

Presented here is a JavaScript async function designed to populate a sudoku board with numbers, essentially solving the puzzle. To enhance the user experience and showcase the recursion and backtracking algorithm in action, a sleeper function is utilized b ...

I am encountering unexpected behavior with NextJS's getInitialProps function, as it is giving me a compiler error stating "varName not found on type {}"

I seem to be stuck on a simple syntax issue while working with NextJs. I am attempting to perform dynamic server-side fetches using the getInitialProps pattern. However, the compiler is unable to recognize the return of getInitialProps in the regular func ...

TypeScript causing issues when multiple selectors are used

Currently, I have a function that allows me to search for HTML elements based on text content: function findElementsByText(text: string): HTMLElement[] { const selectors = 'button' // This conditional statement ensures that an empty text quer ...

Sign up for receiving updates from an observable obtained through the GET method of HttpClient

I'm currently studying Angular and the course material I have is outdated. In my project, I am developing a simple application that displays a list of people created from a Persona class with parameters nombre and apellido. The code snippet is as fol ...

What are the steps to fix the error stating that 'loginError.data' is an unknown type?

Recently delving into typescript, I decided to test the waters with nextjs, rtk query, and antd. However, while attempting to access error within useLoginMutation using the property error.data.message, it was flagged as type unknown. To tackle this issue, ...

Is there a way to reset the state within a child component while leveraging the useContext hook?

After removing unnecessary code, I have shared my updated code below. Initially, everything works smoothly when I click the button in DealWithHands. However, upon clicking the button again, the table in Analysis retains its previous calculation result and ...