22

My await statements inside the async functions are calls to jQuery's $.post() method which return a valid promise, however I am getting this error in TypeScript:

Type of 'await' operand must either be a valid promise or must not contain a callable 'then' member.

My function is this (simplified for the example). The code is valid and works, but I am getting a error in the TS console.

 async function doAsyncPost() {
    const postUrl = 'some/url/';
    const postData = {name: 'foo', value: 'bar'};
    let postResult;
    let upateResult;

    function failed(message: string, body?: string) {
      console.log('error: ', message, ' body: ', body);
    }

    function promiseFunc() {
      return new Promise<void>( resolve => {
        // ... do something else....
        resolve();
      });
    };

    function finish() {
      // ... do something at the end...
    }

    try {
      // The error is on the $.post()
      postResult = await $.post(postUrl, $.param(postData));
      if (postResult.success !== 'true') {
        return failed('Error as occoured', 'Description.....');
      }      
      await promiseFunc();

      return finish();
    } catch (e) {
      await failed('Error as occoured', 'Description.....');
    }
  }

I'm guessing TS is having a problem with $.post() because you can call .then() on it, but how do I get around this problem? Also, I did not have this error prior to updating 2.4.2.

Rory McCrossan
  • 331,213
  • 40
  • 305
  • 339
Amir
  • 4,211
  • 4
  • 23
  • 41
  • Seems indeed typescript is pesky about jQuery returning a promise object which also is a jqXHR object. Try `postResult = await Promise.resolve($.post(postUrl, $.param(postData)));` or, alternatively: `postResult = await $.post(postUrl, $.param(postData)).then();` although the latter would maybe trigger the same error. – trincot Jul 24 '17 at 17:51
  • @trincot both your solutions work. Seems a bit dirty and unnecessary, but the best option so far I guess. If you want to provide your comment as an answer I will mark it as accepted. – Amir Jul 24 '17 at 19:50
  • See also [How to dodge jQuery promises completely in favour of native ones?](https://stackoverflow.com/a/31327725/1048572) – Bergi Aug 12 '20 at 22:28

2 Answers2

31

Seems indeed TypeScript is pesky about jQuery returning a promise object which is both a deferred and a jqXHR object:

The jqXHR objects returned by $.ajax() as of jQuery 1.5 implement the Promise interface, giving them all the properties, methods, and behavior of a Promise (see Deferred object for more information).

There are at least three workarounds to this stubbornness of TypeScript

Solution returning a pure ES6 Promise

You could pass the return value to Promise.resolve(), which will return a true ES6 Promise, promising the same value:

postResult = await Promise.resolve($.post(postUrl, $.param(postData)));

Solutions returning jQuery promises

The other two alternatives do not return pure ES6 promises, but jQuery promises, which still is good enough. Be aware though that these promise objects are only Promises/A+ compliant from jQuery 3 onwards:

You could apply the deferred.promise method, which returns a jQuery promise object:

postResult = await $.post(postUrl, $.param(postData)).promise();

Alternatively, you could apply the deferred.then method, which also returns a jQuery promise:

As of jQuery 1.8, the deferred.then() method returns a new promise

By not providing any argument to then you effectively return a promise for the same promised value:

postResult = await $.post(postUrl, $.param(postData)).then();
trincot
  • 317,000
  • 35
  • 244
  • 286
  • 4
    `$.get(...).then()` didn't work for me, but `$.get(...).promise()` worked fine, and is more readable. – kitsu.eb Apr 23 '18 at 23:05
  • Thanks for mentioning that, @kitsu.eb, I have added the `promise` method solution to my answer. `then` will not work before jQuery 1.8, which might explain why it did not work for you. – trincot Apr 24 '18 at 11:13
1

JQueryXHR has its own version of .then() which has some additional options:

then<R>(doneCallback: (data: any, textStatus: string, jqXHR: JQueryXHR) => R, failCallback?: (jqXHR: JQueryXHR, textStatus: string, errorThrown: any) => void): JQueryPromise<R>;

To use await in TypeScript with $.post, I had to remove that line from jquery.d.ts. TypeScript will then see the .then defined on JQueryGenericPromise.

libertyernie
  • 2,590
  • 1
  • 17
  • 13