Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a new type Awaitable<T> = T | PromiseLike<T> #31394

Closed
5 tasks done
remcohaszing opened this issue May 14, 2019 · 18 comments
Closed
5 tasks done

Add a new type Awaitable<T> = T | PromiseLike<T> #31394

remcohaszing opened this issue May 14, 2019 · 18 comments
Assignees
Labels
Needs Investigation This issue needs a team member to investigate its status.

Comments

@remcohaszing
Copy link

Search Terms

  • awaitable

Suggestion

Add an awaitable type for values that will be awaited.

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

This is based on my question and a comment on StackOverflow

Use Cases

Two use cases come in mind immediately:

  1. A function accepts a callback that may either return a value synchronously, or may return a promise value. This will then probably be awaited.
  2. This is more of a specific version of 1, but this would be the return type of Promise.then() / Promise.catch / Promise.finally callbacks.

Also, this type could replace all 1334 occurrences that come up when running git grep '| Promise' in the current TypeScript code base.

Examples

Callback example:

async function logAnswer(getAnswer: () => Awaitable<number>): Promise<void> {
  const answer = await getAnswer();
  console.log(answer);
}

logAnswer(() => 42);
logAnswer(() => Promise.resolve(42));

Promise example:

Promise.resolve('Hello, world!').then(
  // This type annotation is silly. This is really just to show promise callbacks should accept `Awaitable<T>`.
  (hello): Awaitable<string> => {
    console.log(hello);
    return hello;
  },
);

Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.
@RyanCavanaugh RyanCavanaugh added the Needs Investigation This issue needs a team member to investigate its status. label May 17, 2019
@fatcerberus
Copy link

At first I didn't see the utility of this since everything in JavaScript is awaitable, but I just realized it's good for type inference:

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

declare function foo<T>(callback: () => T): Promise<T>;
declare function bar<T>(callback: () => Awaitable<T>): Promise<T>;

const fun = () => Promise.resolve(812);
let food = foo(fun);  // Promise<Promise<number>>, which is not actually a thing.
let bard = bar(fun);  // Promise<number> - yes!

@fabiospampinato
Copy link

fabiospampinato commented Jul 2, 2019

I think Promisable would be a better name than Awaitable.

Since any value is awaitable in TS this name seems too generic to me, Promisable on the other hand says to me that the value could be actually a Promise (technically a PromiseLike, but PromiseLikeable or PromiseableLike sound awful to me).

@Vandivier
Copy link

I think Promisable and Awaitable are both fair, but I notice that ForAwaitable is another type under discussion and recommend that these two types are aligned regardless of the direction we go.

#36153

@remcohaszing
Copy link
Author

Some feedback from future self: I have been using Promisable a lot. I really hope this will be a builtin type.

david50407 referenced this issue in ChildishGhost/einstein Mar 4, 2021
Control plugin's lifecycle via PluginSetup/PluginDispose
Register plugin event and search engines via PluginContext

TODO: implement plugin searcher/loader for external plugins
TODO: move internal plugins to external packages
@remcohaszing
Copy link
Author

This was added in #45350 and will be part of TypeScript 4.5.

@remcohaszing
Copy link
Author

I misinterpreted it. It’s not the same.

@remcohaszing remcohaszing reopened this Nov 3, 2021
@DanielRosenwasser DanielRosenwasser changed the title Add a new type Awaitable Add a new type Awaitable<T> = T | PromiseLike<T> Nov 19, 2021
@MuTsunTsai
Copy link

I've been using this exact definition in many of my projects for years. It will be great if this is built in.

@crookse
Copy link

crookse commented May 31, 2023

Been using the same Promisable impl from #31394 (comment) since summertime last year. Hoping the same (it becoming a built-in)! It cuts down on a lot of code and improves the developer experience IMO.

@xiBread
Copy link

xiBread commented May 31, 2023

Is this relevant anymore given #51841 (comment)?

@camsteffen
Copy link

I like Awaitable more than Promisable. The whole point of the type is that you can use await on the type to "unwrap" its generic type. It is a corollary to the Awaited type. Promisable to me conveys a semantic like PromiseLike which is not right.

@camsteffen
Copy link

Another name idea is PromiseOr<T>. The Dart language has the same concept but called FutureOr.

@remcohaszing
Copy link
Author

This issue predates that comment and its references. I still think a builtin Awaitable type would be useful, but the TypeScript team is free to close this issue if they feel otherwise.

@RyanCavanaugh RyanCavanaugh closed this as not planned Won't fix, can't repro, duplicate, stale Mar 6, 2024
@camsteffen
Copy link

I know this is closed, but I want to highlight one important argument for this type that hasn't been raised.

The Awaitable type allows you to define function type that is "optionally async". Optionally async functions is a very widely useful pattern.

type NumberCallback = () => Awaitable<number>;

// these both work!
const x: NumberCallback = () => 42;
const y: NumberCallback = async () => 42;

@RyanCavanaugh
Copy link
Member

The merits of an individual utility type aren't really relevant since our position is "No new utility types, at all". If the type is useful for you, you should definitely write it in your project with the definition you'd like it to have.

@btoo
Copy link

btoo commented Mar 27, 2024

@RyanCavanaugh thanks for the update - just for my own learning, is there a decision record rationalizing the "No new utility types, at all" position I can read up on? nevermind, I found it

@MuTsunTsai
Copy link

MuTsunTsai commented Mar 29, 2024

@btoo Not only that there's no such a record, they literally just added a new NoInfer utility type in v5.4 so I have no idea what he's talking about.

@ritschwumm
Copy link

@MuTsunTsai i wouldn't call NoInfer a utility type as you cannot implement it yourself in library code.

@MuTsunTsai
Copy link

@ritschwumm Your opinion is irrelevant. They literally called it "The NoInfer Utility Type".

@microsoft microsoft locked as resolved and limited conversation to collaborators Aug 21, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Needs Investigation This issue needs a team member to investigate its status.
Projects
None yet
Development

No branches or pull requests