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

Allow the use of the async keyword in types to replace Promise #31709

Closed
2 of 5 tasks
AlexAegis opened this issue May 31, 2019 · 7 comments
Closed
2 of 5 tasks

Allow the use of the async keyword in types to replace Promise #31709

AlexAegis opened this issue May 31, 2019 · 7 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@AlexAegis
Copy link

AlexAegis commented May 31, 2019

Search Terms

allow alias async return type promise

Suggestion

Right now if I specify the type of an async function I write foo: () => Promise<string> because that's what it is, fair enough. But it's kind of ugly. The point of async/await is to hide all the ugliness of Promises under syntactic sugar.

TypeScript should do the same and hide the type of the Promise by providing an alternative declaration. Allow us to use the async keyword in type contexts instead of using Promise<?>

So this:

foo: async <T>() => T

could mean the same thing as this:

foo: <T>() => Promise<T>

And would still provide the same IntelliSense as Promise<T>


I've taken into consideration whether it should also allow for T too:

foo: <T>() => Promise<T> | T

My reasoning is that in plain JavaScript awaiting something that's not a Promise just returns that value and TypeScript should respect this.

await "bar" this is valid

This however would completely cripple IntelliSense.
But if it's the case for you, you can always fall back to the <T>() => Promise<T> | T type annotation. (An async? keyword wouldn't really be idiomatic)

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.

EDIT: Took out the checks from the guidelines as I discovered some faults in it.

@AlexAegis AlexAegis changed the title Allow the use of async in return types instead of promise Allow the use of async in types instead of Promise May 31, 2019
@AlexAegis AlexAegis changed the title Allow the use of async in types instead of Promise Allow the use of the async keyword in types to replace Promise May 31, 2019
@leoyli
Copy link

leoyli commented Jun 1, 2019

I think this would be really useful and align with the idiomatic JS quite nicely.

@AlexAegis
Copy link
Author

Came across a use-case today where I allowed this: foo: (input: T) => Promise<R> | R) because foo is sometimes a read from the file system and sometimes it's just a supplier for a plain string (when I'm unit testing said function for example) And realized that the await keyword doesn't care whether you give it a Promise or not.

I updated the issue with my thoughts on the topic, what do you think?.

@nmain
Copy link

nmain commented Jun 3, 2019

With your changes, this becomes related to #31394

@AlexAegis
Copy link
Author

AlexAegis commented Jun 3, 2019

True, but I do think it would be better to use the more strict approach here and infer only Promise<T> from a possible foo: async <T>() => T type implementation.

@AlexAegis
Copy link
Author

Again, this is might not as straightforward as it seems. While in purely in terms of types this is rather simple, the actual definition would still be defined as a Promise<T> and changing this would be a breaking change, with probably many more unseen side-effects.

Current:

const foo: <T>(t: T) => Promise<T> = async <T>(t: T): Promise<T> => {
	return {} as T;
};

The issue with the idea:

const bar: async <T>(t: T) => T = async <T>(t: T): Promise<T> => {
	return {} as T;
};

Only changing the types behavior would lead to some inconsistencies where in the type, async would mean that the return type is wrapped in a Promise while in the actual function definition it's not. This would be really confusing.

For this I think this is not something that would be a welcome change in TypeScript, not even a good one.

I know I went a complete 180° here, but I think TypeScript is a great tool for JavaScript and as such it shouldn't mask the actual behavior of it under inconsistent keywords.

I'm leaving this up so someone from the TypeScript team might see this and give a proper opinion about the topic. I might be wrong in the proposal, I might be wrong here.

@fatcerberus
Copy link

fatcerberus commented Jun 5, 2019

Because 1) Types are erased at compile time, 2) The proposed async is type-level info only, and 3) The most useful case for this IMO is in declaring the types of functions we don't actually know anything about (e.g., callbacks), the only way this makes sense to me is if it means T | PromiseLike<T>, and in that case we can get exactly the same thing out of the Awaitable<T> type proposed in #31394, without the need for additional syntax.

@RyanCavanaugh RyanCavanaugh added the Working as Intended The behavior described is the intended behavior; this is not a bug label Jun 13, 2019
@RyanCavanaugh
Copy link
Member

We went through the same line of thinking when designing the type system functionality for async - forcing you to write Promise<T> is obviously an annoying burden, but it really is the correct thing to do

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

5 participants