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 the 'awaited' type operator #35998

Merged
merged 25 commits into from Mar 20, 2020
Merged

Add the 'awaited' type operator #35998

merged 25 commits into from Mar 20, 2020

Conversation

rbuckton
Copy link
Member

@rbuckton rbuckton commented Jan 4, 2020

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 native Promise 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:

T extends PromiseLike<infer U> ? { 0: Awaited<U> }[U extends any ? 0 : never ] : ...

However, there are a number of problems with this approach:

  • It is not obvious what { 0: ... }[U extends any ? 0 : never] is doing in this alias.
  • The index access type approach is a bit of a "hack" that we probably should not include in our core libs.
  • A self-recursive "promise" type can easily exhaust the maximum recursion limit, resulting in errors in the type alias itself, rather than in the offending code that references it.
  • The quick-info for such a type is non-trivial.

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 like Promise.all, Promise.race, Promise.allSettled, Promise.prototype.then, and Promise.prototype.catch. The awaited type operator has the following semantics:

  • If the type operand is non-generic, the awaited operator is resolved immediately:
    • awaited number is number
    • awaited Promise<number> is number
  • If the type operand is generic, the awaited operator is deferred until instantiated with a non-generic:
    • awaited Promise<T> is awaited T
    • awaited Promise<Obj[K]> is awaited Obj[K]
  • An awaited S is assignable to an awaited T if S is assignable to T:
    • CAVEAT: This isn't precisely sound, as a non-thenable T could be subtyped with an S that has a then(). However, this is not likely to be a common practice and is no less permissive than the pseudo-recursive conditional type approach.
  • An awaited S is related to T if awaited C is related to T, where C is the constraint of S:
    function f<T extends number>(x: awaited T) {
      const y: number = x; // ok, `awaited number` is assignable to `number`
    }

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.

@rbuckton
Copy link
Member Author

rbuckton commented Jan 4, 2020

cc: @weswigham, @DanielRosenwasser, @ahejlsberg, @RyanCavanaugh (since suggested reviewers weren't working)

@dragomirtitian
Copy link
Contributor

Wouldn't a more general operator, something like unwrap be of broader use ? I know of a couple of people trying to unwrap Array for example. Something like T unwrap G<infer> (with infer getting only one use). With the inheritance rules being tied to the variance of G.

@falsandtru
Copy link
Contributor

Looks like the position of type argument declarations is not the best for inserting modifiers. So if we use unwrap, it would be as follows:

function f<T extends number>(x: unwrapped PrimiseLike<T>) {
}

@dragomirtitian
Copy link
Contributor

@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.

@jablko
Copy link
Contributor

jablko commented Jan 5, 2020

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?

@rbuckton
Copy link
Member Author

rbuckton commented Jan 6, 2020

Wouldn't a more general operator, something like unwrap be of broader use ? I know of a couple of people trying to unwrap Array for example. Something like T unwrap G<infer> (with infer getting only one use). With the inheritance rules being tied to the variance of G.

Its not that simple, for several reasons:

  • Type Aliases cannot reference themselves recursively (without using the indexed access hack).
  • An unwrap operator could easily result in infinite recursion, and would therefore need to have a max recursion limit.
  • Its not easy to indicate how to handle bad actors like self-recursive or mutually-recursive interfaces.

All of these behaviors are already handled by await in the compiler today specifically for handling Promise-like types, and the awaited type operator is formalization of this behavior in the type system. This would not preclude us from introducing a separate mechanism for unrolling a recursive type in the future.

@rbuckton
Copy link
Member Author

rbuckton commented Jan 6, 2020

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?

We already investigated adding an Awaited<T> to the core library and abandoned that approach. The problem being that we cannot currently model the recursive unwrap of the await operator with a type alias. The only way that could be remotely feasible is if we had a mechanism to forbid Promise<Promise<T>>, but such a mechanism also does not yet exist.

@jablko
Copy link
Contributor

jablko commented Jan 6, 2020

Is it necessary for Awaited<T> to be recursive, if the only way to get a nested promise is to explicitly write it as such, which is out of scope?

Why make it recursive if Promise<Promise<T>> will someday be forbidden?

@treybrisbane
Copy link

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? :)

@rbuckton
Copy link
Member Author

I can't help but feel as though we're jumping too quickly to a syntactic solution here...

I would argue that two years isn't "jumping too quickly". Adding awaited T doesn't preclude us from investigating recursive conditional types. The problem with a recursive type alias is the "halting problem": Adding that level of complexity can make the type system unpredictable as it can quickly become impossible to be able to reason over whether a type can be instantiated in finite time or using finite memory. Any solution for recursive type aliases would need to be very restrictive.

@rbuckton
Copy link
Member Author

Is it necessary for Awaited to be recursive, if the only way to get a nested promise is to explicitly write it as such, which is out of scope?

That wasn't what was out of scope. What I was calling out was that writing Promise<Promise<number>> wouldn't be automatically replaced by Promise<number> by the compiler.

@falsandtru
Copy link
Contributor

I agree, awaited is not smart but inevitable. Since it is difficult to generalize unwrapping, we have to specify the kind of unwrapping like awaited.

@treybrisbane
Copy link

I would argue that two years isn't "jumping too quickly". Adding awaited T doesn't preclude us from investigating recursive conditional types.

This is true. 🙂
If we feel that this is the best way to address the issue, then let's do it. Just want us to be extra sure that a syntactic solution is the right one.

The problem with a recursive type alias is the "halting problem"

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 awaited syntax in the first place (which is why I brought it up).

@jablko
Copy link
Contributor

jablko commented Jan 16, 2020

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?

@ExE-Boss
Copy link
Contributor

ExE-Boss commented Jan 22, 2020

@rbuckton Can’t it just be await <type>?

@jablko
Copy link
Contributor

jablko commented Jan 22, 2020

My argument is that

  • The purpose of a recursive solution is to accommodate Promise<Promise<T>>.
  • The only way to get Promise<Promise<T>> is to explicitly write it as such.
  • Writing Promise<Promise<T>> is an error. It should be written Promise<T>. They are the same thing.
    • A recursive awaited solution doesn't solve the problems with writing Promise<Promise<T>>. It remains unassignable/comparable to Promise<T>.
    • Will it ever be assignable/comparable to Promise<T>? To me, that seems even less justified than introducing a recursive awaited solution?

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?

@ExE-Boss
Copy link
Contributor

I’d prefer if the type operator was just await T, as it’d make sense to me that the result of an await expression would be await <type>, rather than awaited <type>.

@sandersn sandersn added this to Gallery in Pall Mall Jan 28, 2020
@sandersn sandersn moved this from Gallery to Bête Noire in Pall Mall Jan 28, 2020
@sandersn sandersn added the Experiment A fork with an experimental idea which might not make it into master label Feb 1, 2020
@rbuckton
Copy link
Member Author

@jablko: The problem of Promise<Promise<T>> isn't just the fact you can explicitly write such a type, but that it's relatively easy to accidentally end up with such a type when composing higher-order functions.

@typescript-bot
Copy link
Collaborator

@rbuckton
The results of the perf run you requested are in!

Here they are:

Comparison Report - master..35998

Metric master 35998 Delta Best Worst
Angular - node (v10.16.3, x64)
Memory used 327,899k (± 0.03%) 327,532k (± 0.03%) -367k (- 0.11%) 327,281k 327,784k
Parse Time 1.63s (± 0.74%) 1.62s (± 0.51%) -0.01s (- 0.55%) 1.59s 1.63s
Bind Time 0.89s (± 1.06%) 0.88s (± 0.51%) -0.01s (- 0.90%) 0.87s 0.89s
Check Time 4.76s (± 0.46%) 4.76s (± 0.32%) +0.00s (+ 0.08%) 4.73s 4.79s
Emit Time 5.29s (± 0.55%) 5.32s (± 1.36%) +0.03s (+ 0.53%) 5.21s 5.57s
Total Time 12.57s (± 0.27%) 12.58s (± 0.56%) +0.01s (+ 0.08%) 12.47s 12.82s
Monaco - node (v10.16.3, x64)
Memory used 327,099k (± 0.02%) 327,116k (± 0.01%) +17k (+ 0.01%) 327,021k 327,202k
Parse Time 1.25s (± 0.74%) 1.26s (± 0.53%) +0.01s (+ 0.88%) 1.25s 1.28s
Bind Time 0.78s (± 0.88%) 0.77s (± 0.67%) -0.00s (- 0.64%) 0.76s 0.78s
Check Time 4.74s (± 0.50%) 4.74s (± 0.36%) -0.00s (- 0.06%) 4.71s 4.80s
Emit Time 2.94s (± 0.86%) 2.92s (± 0.99%) -0.02s (- 0.71%) 2.88s 3.01s
Total Time 9.71s (± 0.38%) 9.69s (± 0.41%) -0.02s (- 0.16%) 9.63s 9.79s
TFS - node (v10.16.3, x64)
Memory used 291,942k (± 0.02%) 292,005k (± 0.01%) +63k (+ 0.02%) 291,946k 292,063k
Parse Time 0.95s (± 0.55%) 0.95s (± 0.86%) -0.00s (- 0.11%) 0.93s 0.97s
Bind Time 0.75s (± 0.89%) 0.74s (± 0.60%) -0.01s (- 0.67%) 0.73s 0.75s
Check Time 4.29s (± 0.67%) 4.27s (± 0.60%) -0.02s (- 0.54%) 4.20s 4.34s
Emit Time 3.05s (± 0.84%) 3.06s (± 0.76%) +0.01s (+ 0.33%) 3.02s 3.12s
Total Time 9.04s (± 0.52%) 9.02s (± 0.36%) -0.02s (- 0.25%) 8.97s 9.10s
material-ui - node (v10.16.3, x64)
Memory used 453,243k (± 0.01%) 452,995k (± 0.01%) -248k (- 0.05%) 452,862k 453,137k
Parse Time 1.78s (± 0.54%) 1.77s (± 0.28%) -0.01s (- 0.39%) 1.76s 1.78s
Bind Time 0.68s (± 0.54%) 0.69s (± 0.72%) +0.00s (+ 0.15%) 0.68s 0.70s
Check Time 13.78s (± 0.75%) 13.67s (± 0.41%) -0.11s (- 0.78%) 13.54s 13.81s
Emit Time 0.00s (± 0.00%) 0.00s (± 0.00%) 0.00s ( NaN%) 0.00s 0.00s
Total Time 16.25s (± 0.65%) 16.13s (± 0.36%) -0.12s (- 0.71%) 15.99s 16.27s
Angular - node (v12.1.0, x64)
Memory used 303,529k (± 0.07%) 303,002k (± 0.10%) -527k (- 0.17%) 302,104k 303,309k
Parse Time 1.58s (± 0.55%) 1.57s (± 0.62%) -0.01s (- 0.57%) 1.55s 1.59s
Bind Time 0.88s (± 0.84%) 0.87s (± 0.74%) -0.01s (- 0.91%) 0.86s 0.89s
Check Time 4.66s (± 0.62%) 4.66s (± 0.83%) +0.01s (+ 0.13%) 4.59s 4.76s
Emit Time 5.50s (± 0.97%) 5.48s (± 1.90%) -0.02s (- 0.35%) 5.38s 5.88s
Total Time 12.61s (± 0.57%) 12.59s (± 1.04%) -0.03s (- 0.22%) 12.45s 13.04s
Monaco - node (v12.1.0, x64)
Memory used 307,057k (± 0.02%) 307,045k (± 0.01%) -12k (- 0.00%) 306,964k 307,151k
Parse Time 1.22s (± 0.61%) 1.20s (± 0.41%) -0.01s (- 1.07%) 1.19s 1.21s
Bind Time 0.75s (± 1.09%) 0.74s (± 0.90%) -0.00s (- 0.54%) 0.73s 0.76s
Check Time 4.56s (± 0.28%) 4.57s (± 0.61%) +0.01s (+ 0.29%) 4.52s 4.65s
Emit Time 2.95s (± 0.72%) 2.96s (± 1.09%) +0.01s (+ 0.37%) 2.89s 3.04s
Total Time 9.47s (± 0.25%) 9.48s (± 0.52%) +0.01s (+ 0.13%) 9.36s 9.61s
TFS - node (v12.1.0, x64)
Memory used 274,245k (± 0.02%) 274,349k (± 0.03%) +104k (+ 0.04%) 274,243k 274,640k
Parse Time 0.93s (± 0.89%) 0.93s (± 0.80%) -0.00s (- 0.43%) 0.91s 0.94s
Bind Time 0.70s (± 0.70%) 0.70s (± 0.95%) -0.00s (- 0.57%) 0.69s 0.71s
Check Time 4.18s (± 0.50%) 4.20s (± 0.51%) +0.02s (+ 0.41%) 4.15s 4.25s
Emit Time 3.09s (± 0.85%) 3.09s (± 0.79%) +0.00s (+ 0.13%) 3.04s 3.13s
Total Time 8.90s (± 0.46%) 8.92s (± 0.42%) +0.02s (+ 0.19%) 8.84s 9.02s
material-ui - node (v12.1.0, x64)
Memory used 430,694k (± 0.01%) 430,368k (± 0.05%) -327k (- 0.08%) 429,575k 430,560k
Parse Time 1.76s (± 0.59%) 1.75s (± 0.57%) -0.01s (- 0.28%) 1.74s 1.78s
Bind Time 0.63s (± 1.29%) 0.63s (± 0.71%) -0.00s (- 0.32%) 0.62s 0.64s
Check Time 12.16s (± 0.60%) 12.21s (± 0.64%) +0.05s (+ 0.44%) 12.06s 12.43s
Emit Time 0.00s (± 0.00%) 0.00s (± 0.00%) 0.00s ( NaN%) 0.00s 0.00s
Total Time 14.55s (± 0.57%) 14.59s (± 0.58%) +0.04s (+ 0.30%) 14.41s 14.82s
Angular - node (v8.9.0, x64)
Memory used 322,924k (± 0.01%) 322,654k (± 0.02%) -270k (- 0.08%) 322,557k 322,808k
Parse Time 2.12s (± 0.50%) 2.11s (± 0.46%) -0.01s (- 0.28%) 2.09s 2.14s
Bind Time 0.92s (± 0.74%) 0.91s (± 0.49%) -0.01s (- 1.30%) 0.90s 0.92s
Check Time 5.47s (± 1.28%) 5.48s (± 0.53%) +0.01s (+ 0.15%) 5.42s 5.55s
Emit Time 6.31s (± 1.33%) 6.22s (± 0.58%) -0.09s (- 1.47%) 6.16s 6.30s
Total Time 14.82s (± 0.38%) 14.72s (± 0.33%) -0.10s (- 0.66%) 14.60s 14.81s
Monaco - node (v8.9.0, x64)
Memory used 325,413k (± 0.02%) 325,495k (± 0.01%) +81k (+ 0.02%) 325,403k 325,613k
Parse Time 1.55s (± 0.58%) 1.54s (± 0.39%) -0.01s (- 0.58%) 1.52s 1.55s
Bind Time 0.89s (± 0.75%) 0.90s (± 0.93%) +0.00s (+ 0.22%) 0.88s 0.91s
Check Time 5.36s (± 0.31%) 5.38s (± 0.54%) +0.02s (+ 0.32%) 5.32s 5.45s
Emit Time 3.51s (± 0.41%) 3.51s (± 0.48%) +0.00s (+ 0.09%) 3.48s 3.54s
Total Time 11.31s (± 0.29%) 11.32s (± 0.32%) +0.02s (+ 0.14%) 11.24s 11.40s
TFS - node (v8.9.0, x64)
Memory used 291,296k (± 0.01%) 291,360k (± 0.01%) +64k (+ 0.02%) 291,298k 291,481k
Parse Time 1.25s (± 0.52%) 1.26s (± 0.39%) +0.00s (+ 0.32%) 1.25s 1.27s
Bind Time 0.75s (± 0.64%) 0.75s (± 0.69%) +0.00s (+ 0.54%) 0.74s 0.76s
Check Time 4.90s (± 1.59%) 4.88s (± 1.26%) -0.03s (- 0.53%) 4.77s 5.05s
Emit Time 3.25s (± 2.49%) 3.32s (± 2.24%) +0.07s (+ 2.25%) 3.08s 3.42s
Total Time 10.15s (± 0.50%) 10.20s (± 0.32%) +0.06s (+ 0.56%) 10.12s 10.26s
material-ui - node (v8.9.0, x64)
Memory used 455,815k (± 0.01%) 455,603k (± 0.01%) -212k (- 0.05%) 455,505k 455,716k
Parse Time 2.11s (± 0.58%) 2.10s (± 0.44%) -0.01s (- 0.66%) 2.08s 2.12s
Bind Time 0.81s (± 0.58%) 0.81s (± 1.17%) +0.00s (+ 0.49%) 0.79s 0.83s
Check Time 17.68s (± 0.56%) 17.79s (± 0.74%) +0.12s (+ 0.66%) 17.51s 18.07s
Emit Time 0.00s (± 0.00%) 0.00s (± 0.00%) 0.00s ( NaN%) 0.00s 0.00s
Total Time 20.60s (± 0.51%) 20.71s (± 0.64%) +0.11s (+ 0.53%) 20.44s 21.02s
Angular - node (v8.9.0, x86)
Memory used 185,761k (± 0.02%) 185,632k (± 0.02%) -129k (- 0.07%) 185,516k 185,699k
Parse Time 2.06s (± 0.81%) 2.04s (± 0.54%) -0.02s (- 0.97%) 2.02s 2.06s
Bind Time 1.07s (± 0.82%) 1.07s (± 0.85%) -0.00s (- 0.09%) 1.06s 1.10s
Check Time 5.01s (± 0.76%) 5.04s (± 0.55%) +0.03s (+ 0.52%) 4.98s 5.09s
Emit Time 6.02s (± 0.46%) 6.05s (± 0.44%) +0.03s (+ 0.47%) 5.99s 6.11s
Total Time 14.16s (± 0.46%) 14.19s (± 0.27%) +0.03s (+ 0.24%) 14.10s 14.29s
Monaco - node (v8.9.0, x86)
Memory used 185,266k (± 0.01%) 185,273k (± 0.03%) +8k (+ 0.00%) 185,160k 185,391k
Parse Time 1.59s (± 0.66%) 1.58s (± 0.41%) -0.01s (- 0.50%) 1.57s 1.60s
Bind Time 0.77s (± 0.67%) 0.76s (± 0.49%) -0.01s (- 0.78%) 0.76s 0.77s
Check Time 5.42s (± 0.54%) 5.38s (± 0.96%) -0.04s (- 0.66%) 5.19s 5.45s
Emit Time 2.89s (± 0.88%) 2.92s (± 3.36%) +0.03s (+ 1.00%) 2.83s 3.30s
Total Time 10.67s (± 0.42%) 10.65s (± 0.58%) -0.02s (- 0.20%) 10.53s 10.85s
TFS - node (v8.9.0, x86)
Memory used 166,706k (± 0.03%) 166,774k (± 0.03%) +68k (+ 0.04%) 166,689k 166,863k
Parse Time 1.29s (± 0.81%) 1.29s (± 0.74%) -0.00s (- 0.16%) 1.27s 1.31s
Bind Time 0.72s (± 1.22%) 0.72s (± 0.77%) -0.00s (- 0.28%) 0.71s 0.73s
Check Time 4.60s (± 0.39%) 4.65s (± 0.64%) +0.04s (+ 0.96%) 4.57s 4.72s
Emit Time 3.05s (± 3.32%) 3.01s (± 1.17%) -0.04s (- 1.28%) 2.90s 3.06s
Total Time 9.66s (± 1.16%) 9.66s (± 0.41%) -0.00s (- 0.01%) 9.56s 9.76s
material-ui - node (v8.9.0, x86)
Memory used 257,798k (± 0.01%) 257,710k (± 0.02%) -89k (- 0.03%) 257,544k 257,797k
Parse Time 2.18s (± 0.63%) 2.19s (± 0.76%) +0.01s (+ 0.64%) 2.16s 2.22s
Bind Time 0.68s (± 1.69%) 0.68s (± 1.03%) -0.00s (- 0.29%) 0.67s 0.70s
Check Time 16.26s (± 0.57%) 16.34s (± 0.49%) +0.08s (+ 0.49%) 16.20s 16.51s
Emit Time 0.00s (± 0.00%) 0.00s (± 0.00%) 0.00s ( NaN%) 0.00s 0.00s
Total Time 19.12s (± 0.49%) 19.21s (± 0.45%) +0.09s (+ 0.49%) 19.04s 19.37s
System
Machine Namets-ci-ubuntu
Platformlinux 4.4.0-166-generic
Architecturex64
Available Memory16 GB
Available Memory1 GB
CPUs4 × Intel(R) Core(TM) i7-4770 CPU @ 3.40GHz
Hosts
  • node (v10.16.3, x64)
  • node (v12.1.0, x64)
  • node (v8.9.0, x64)
  • node (v8.9.0, x86)
Scenarios
  • Angular - node (v10.16.3, x64)
  • Angular - node (v12.1.0, x64)
  • Angular - node (v8.9.0, x64)
  • Angular - node (v8.9.0, x86)
  • Monaco - node (v10.16.3, x64)
  • Monaco - node (v12.1.0, x64)
  • Monaco - node (v8.9.0, x64)
  • Monaco - node (v8.9.0, x86)
  • TFS - node (v10.16.3, x64)
  • TFS - node (v12.1.0, x64)
  • TFS - node (v8.9.0, x64)
  • TFS - node (v8.9.0, x86)
  • material-ui - node (v10.16.3, x64)
  • material-ui - node (v12.1.0, x64)
  • material-ui - node (v8.9.0, x64)
  • material-ui - node (v8.9.0, x86)
Benchmark Name Iterations
Current 35998 10
Baseline master 10

@rbuckton
Copy link
Member Author

declare function f<T>(x: Promise<T>): Promise<T extends number ? true : false>;
const expected: Promise<true> = f(undefined as Promise<Promise<number>>);

...

Sorry: Now that Promise<Promise<number>> is assignable to Promise<T>, what is T? number or Promise<number>?

This will result in an error as we haven't changed inference here. T is still inferred as Promise<number>, so your return type in the conditional is instantiated as Promise<Promise<number> extends number ? true : false> which becomes Promise<false>.

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.

@rbuckton
Copy link
Member Author

@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.

@rbuckton
Copy link
Member Author

Upon investigation, I think the fix will need to be in the JQuery types, due to the fact they are extending from both PromiseLike and copy of Promise.

@jablko
Copy link
Contributor

jablko commented Mar 20, 2020

T is still inferred as Promise<number>

Would it make sense to accommodate Promise<Promise<number>> in this context and if so, what would be the differences between awaited T and

type Awaited<T> = Promise<T> extends Promise<infer U> ? U : never;

Assuming U would become awaited T?

@rbuckton
Copy link
Member Author

T is still inferred as Promise<number>

Would it make sense to accommodate Promise<Promise<number>> in this context [...]

Currently, no. We're not doing anything special for conditionals here, and as I mentioned the problem would be easily addressable by using awaited T in your example.

[...] and if so, what would be the differences between awaited T and

type Awaited<T> = Promise<T> extends Promise<infer U> ? U : never;

Assuming U would become awaited T?

I'm not sure what the difference would be, and I don't see how we could choose to assume the U in Promise<infer U> should be awaited T.

@rbuckton
Copy link
Member Author

There are some known breaks on DT that I am planning to address in parallel.

@rbuckton rbuckton merged commit e3ec7b1 into master Mar 20, 2020
sheetalkamat added a commit to microsoft/TypeScript-TmLanguage that referenced this pull request Mar 20, 2020
@ExE-Boss
Copy link
Contributor

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

@rbuckton
Copy link
Member Author

@ExE-Boss thanks, I went ahead and closed them manually.

kdesysadmin pushed a commit to KDE/syntax-highlighting that referenced this pull request Apr 15, 2020
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Author: Team Experiment A fork with an experimental idea which might not make it into master
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Promise<Promise<T>> cannot exist in JS