-
Notifications
You must be signed in to change notification settings - Fork 12.8k
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Add the 'awaited' type operator #35998
Conversation
cc: @weswigham, @DanielRosenwasser, @ahejlsberg, @RyanCavanaugh (since suggested reviewers weren't working) |
Wouldn't a more general operator, something like |
Looks like the position of type argument declarations is not the best for inserting modifiers. So if we use function f<T extends number>(x: unwrapped PrimiseLike<T>) {
} |
@falsandtru I think there are complications in the syntax. For example what if you need to unwrap a type with more than one type parameter, you need to be able to specify the position of the argument that you are unwrapping by. |
If nested promises are allowed, then shouldn't the following be allowed? const expected: Promise<string> = undefined as Promise<Promise<string>>;
~~~~~~~~
!!! error TS2322: Type 'Promise<Promise<string>>' is not assignable to type 'Promise<string>'. What do you think of #35284 instead? |
Its not that simple, for several reasons:
All of these behaviors are already handled by |
We already investigated adding an |
Is it necessary for Why make it recursive if |
I can't help but feel as though we're jumping too quickly to a syntactic solution here... From what I understand of this issue, we could hypothetically solve this problem without new syntax if we had a "canonized" story around recursive conditional types. Is that a reasonable assessment? Anecdotally, I've seen the topic of recursive types come up a lot, and I know there are a good few libraries available that implement various forms of the hack @rbuckton mentioned. It definitely feels to me as though there's need for a more general feature (or set of features) in the recursive type space. Has there been any exploration/ideation into that space that would address this specific issue as part of a more general solution? :) |
I would argue that two years isn't "jumping too quickly". Adding |
That wasn't what was out of scope. What I was calling out was that writing |
I agree, |
This is true. 🙂
Oh 100%. I don't mean to trivialise recursive types, as I'm very aware of the headaches they bring. 🙂 But people are writing them already (myself included), and there are libraries full of them. If we could build out that feature space a bit, then it's possible we wouldn't need special |
What are the cases where a non-recursive solution isn't equivalent? Why are those cases important? Do they justify adding a new kind of type? |
@rbuckton Can’t it just be |
My argument is that
It's clear why #21613 was investigated and rejected, if a recursive solution was assumed. What I'm searching for and haven't found yet, is why a non-recursive solution was rejected? |
I’d prefer if the type operator was just |
@jablko: The problem of |
To me, The word |
I agree. So I agreed with |
@rbuckton Here they are:Comparison Report - master..35998
System
Hosts
Scenarios
|
...
This will result in an error as we haven't changed inference here. However, if you change your example in this way: declare function f<T>(x: Promise<T>): Promise<awaited T extends number ? true : false>;
const expected: Promise<true> = f(undefined as any as Promise<Promise<number>>); Then you get the correct result. |
@DanielRosenwasser in the DT test I'm seeing a lot of this from JQuery: ../jquery/misc.d.ts(1205,15): error TS2430: Interface 'PromiseBase<TR, TJ, TN, UR, UJ, UN, VR, VJ, VN, SR, SJ, SN>' incorrectly extends interface '_Promise<TR>'.
Types of property 'then' are incompatible.
Type '{ <ARD = never, AJD = never, AND = never, BRD = never, BJD = never, BND = never, CRD = never, CJD = never, CND = never, RRD = never, RJD = never, RND = never, ARF = never, AJF = never, ANF = never, BRF = never, BJF = never, BNF = never, CRF = never, CJF = never, CNF = never, RRF = never, RJF = never, RNF = never, AR...' is not assignable to type '<TResult1 = TR, TResult2 = never>(onfulfilled?: ((value: TR) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<...>) | null | undefined) => _Promise<...>'. I'm investigating whether this is something I can fix in this PR or if I will just need to make a change to JQuery. |
Upon investigation, I think the fix will need to be in the JQuery types, due to the fact they are extending from both |
Would it make sense to accommodate type Awaited<T> = Promise<T> extends Promise<infer U> ? U : never; Assuming |
Currently, no. We're not doing anything special for conditionals here, and as I mentioned the problem would be easily addressable by using
I'm not sure what the difference would be, and I don't see how we could choose to assume the |
There are some known breaks on DT that I am planning to address in parallel. |
To close multiple issues or PRs, you need to write the keyword before each of them: https://help.github.com/en/github/managing-your-work-on-github/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword |
@ExE-Boss thanks, I went ahead and closed them manually. |
Summary: Add the new keyword "awaited": microsoft/TypeScript#35998 Reviewers: #framework_syntax_highlighting, dhaumann, cullmann Reviewed By: #framework_syntax_highlighting, cullmann Subscribers: kwrite-devel, kde-frameworks-devel Tags: #kate, #frameworks Differential Revision: https://phabricator.kde.org/D28814
It has been over two years since we decided not to move forward with #17077 due to the advent of conditional types, however in that time we have still been unable to devise a reliable mechanism for defining a recursive conditional type that can properly handle the type-side of ECMAScript's
await
and nativePromise
unwrapping behavior.Solutions like
Awaited<T>
were untenable given that the only way to make the conditional type "recursive" was to introduce complex type like the following:However, there are a number of problems with this approach:
{ 0: ... }[U extends any ? 0 : never]
is doing in this alias.As a result, I would like to reintroduce the
awaited
type operator from #17077 to meet our needs for a mechanism to recursively unwrap a Promise-like type for methods likePromise.all
,Promise.race
,Promise.allSettled
,Promise.prototype.then
, andPromise.prototype.catch
. Theawaited
type operator has the following semantics:awaited
operator is resolved immediately:awaited number
isnumber
awaited Promise<number>
isnumber
awaited
operator is deferred until instantiated with a non-generic:awaited Promise<T>
isawaited T
awaited Promise<Obj[K]>
isawaited Obj[K]
awaited S
is assignable to anawaited T
ifS
is assignable toT
:T
could be subtyped with anS
that has athen()
. However, this is not likely to be a common practice and is no less permissive than the pseudo-recursive conditional type approach.awaited S
is related toT
ifawaited C
is related toT
, whereC
is the constraint ofS
:This will help us to merge PRs such as #33707 without needing to introduce a non-recursive or pseudo-recursive
Awaited<T>
type.Fixes: #27711, #31722
Closes: #36435, #36368.