Awaitable type in TypeScript
Asked Answered
E

3

8

I use async / await a lot in JavaScript. Now I’m gradually converting some parts of my code bases to TypeScript.

In some cases my functions accept a function that will be called and awaited. This means it may either return a promise, just a synchronous value. I have defined the Awaitable type for this.

type Awaitable<T> = T | Promise<T>;

async function increment(getNumber: () => Awaitable<number>): Promise<number> {
  const num = await getNumber();
  return num + 1;
}

It can be called like this:

// logs 43
increment(() => 42).then(result => {console.log(result)})

// logs 43
increment(() => Promise.resolve(42)).then(result => {console.log(result)})

This works. However, it is annoying having to specify Awaitable for all of my projects that use async/await and TypeScript.

I can’t really believe such a type isn’t built in, but I couldn’t find one. Does TypeScript have a builtin awaitable type?

Epidemic answered 7/5, 2019 at 11:27 Comment(8)
What exactly is the problem? Why do you need to specify Awaitable everywhere in your codebase? Either your function returns T or Promise<T>, either way your function can handle itEquilibrant
Also type Awaitable<T> = T | Promise<T> isn't that complicated that writing it in multiple projects would that big of a problemEquilibrant
@Equilibrant But if you have multiple return lines, you would have to wrap every synchronized value in a promise, which is not bad, but you don't have to.Sickly
@Sickly what do you mean by multiple return lines and every synchronised value? Could you give an example?Equilibrant
@Equilibrant It is sort of what was added to the post. () => 42 is invalid if you use Promise<T> as the signature, but you can just call await 42 in typescript.Sickly
I quite like your Awaitable<T>. Might be worth doing an issue on the TypeScript issue list and a pull request adding it to lib.es5.d.ts. :-)Ocelot
@T.J.Crowder I created an issueEpidemic
Is this actually the reason for the Feature Request of the awaited operator in TS3.9 that did not make is into the final release? github.com/microsoft/TypeScript/pull/35998 It seems to exactly fill this gapRebuttal
O
5

I believe the answer to this question is: No, there is no built-in type for this.

In lib.es5.d.ts and lib.es2015.promise.d.ts, they use T | PromiseLike<T> for the various places your Awaitable<T> would make sense, for instance:

/**
 * Represents the completion of an asynchronous operation
 */
interface Promise<T> {
    /**
     * Attaches callbacks for the resolution and/or rejection of the Promise.
     * @param onfulfilled The callback to execute when the Promise is resolved.
     * @param onrejected The callback to execute when the Promise is rejected.
     * @returns A Promise for the completion of which ever callback is executed.
     */
    then<TResult1 = T, TResult2 = never>(onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null): Promise<TResult1 | TResult2>;

    /**
     * Attaches a callback for only the rejection of the Promise.
     * @param onrejected The callback to execute when the Promise is rejected.
     * @returns A Promise for the completion of the callback.
     */
    catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null): Promise<T | TResult>;
}

There's nothing like your Awaitable in lib.es5.d.ts where they define PromiseLike and Promise.

I'd think if they'd defined one, they would use it in those definitions.

Side note: Based on those definitions, it probably makes sense to use PromiseLike rather than Promise in your Awaitable:

type Awaitable<T> = T | PromiseLike<T>;
Ocelot answered 14/5, 2019 at 11:37 Comment(0)
O
0

As T.J. Crowder pointed out, it's best to use PromiseLike(T) | T. Using PromiseLike instead of Promise allows objects that conform to the promise interface without being an instance of the built-in Promise type.

There is indeed no built-in utility type for this, but ts-essentials provides an AsyncOrSync type, defined like so:

type AsyncOrSync<Type> = PromiseLike<Type> | Type;

In your function, you'd use it like this:

import type { AsyncOrSync } from "ts-essentials";

async function increment(getNumber: () => AsyncOrSync<number>): Promise<number> {
  const num = await getNumber();
  return num + 1;
}
Occasional answered 5/7, 2024 at 11:29 Comment(0)
T
-1
  1. async/await will always cause the statement to be wrapped into a promise, so your function will always return a promise.
  2. everything can be awaited, regardless of it being async or not, so a custom Awaitable type might just be redundant...
async function test() {
    const foo = await 5;
    console.log(foo);

    const bar = await 'Hello World';
    console.log(bar);

    const foobar = await Promise.resolve('really async');
    console.log(foobar);
}

test();

Link to the ts playground

You don't need extra typing imho, since your function will always have:

async function foo<T>(task: () => T | Promise<T>): Promise<T> {
  const result = await task();

  return result;
}
Theism answered 7/5, 2019 at 11:34 Comment(8)
I don't see how this answers the question. The OP is clearly aware of how await works and that you can await a non-thenable. The question is about defining a type for a callback so that the function provided for the callback parameter may be either a function return T or a function returning Promise<T>.Ocelot
it should help because: 1. Awaitable is redundant, since everything is awaitable by default; 2. as soon as you use the async/await keyword, then your function will always return a promise, hence the typing is always consistent (imho).Theism
No, Awaitable isn't redundant; example.Ocelot
(Continuing my comment above since the link eats up the comment limit.) OP specifically wants to cater to a callback that returns a promise and also one that doesn't, with proper typing.Ocelot
@T.J.Crowder is correct. I want to specify the return type of the callback function that will be awaited.Epidemic
here is fixed version linkCounterrevolution
await always return Promise. so just declare it as promiseCounterrevolution
@JurajKocan - Please read my comment above.Ocelot

© 2022 - 2025 — McMap. All rights reserved.