Update on Collapsing Intersections of Impossible Discriminated Unions
#36696
- Turns impossible intersections into nevers
- Submitted a few PRs on DefinitelyTyped and ts-toolbelt
- RWC has one break which is better errors
- Has this PR run on material-ui in the perf test suite?
- Not yet! Let's try it now.
- The last thing that we found was a break in Office UI Fabric for React
- When you're not in strictNullChecks, we don't add undefined to the type of optional properties.
- So an intersection where a property is optional should not be considered a discriminant.
- Does this only trigger for intersections where all properties are optional?
- Things to keep in mind: IteratorResult uses undefined as a discriminant
- Is a break
- But found a lot of better behavior
- Lots of people doing & to create filters.
- Intersections used to produce absolute garbage for discriminated unions - now we correctly filter them down.
- Wait, why is there a different error baseline for some mapped type thing in the PR?
- New change we've wanted to do for a long time.
- Today, when we resolve the members of a mapped type, we also decide to instantiate each of their respective types.
- But now diving into intersections means that we need to probe a little bit deeper to figure out if it needs to reduce, but that can trigger some circularities in exploring types.
- A test in Redux ORM test had a bunch of issues in inference because of recursive types - doing the intersection reduction cause circularity issues, this deferral on mapped type property types fixes this.
- Helps material-ui by 10%, though the intersection work offsets this a bit.
Update on awaited
#35998
-
Looking to reintroduce the awaited type
-
Held off on this because people wanted to do recursive digging on any type
- Thought conditionals would fix this.
- It doesn't!
-
We had a ton of PR s to fix up Promise.all, Promise.race, etc.
- All of them broken in different wonderful ways.
-
Have one assignability rule that's not sound but is useful: awaited S relates to awaited T if S relates to T
- Not sound because subtypes might introduce a then() method.
- But practically doing otherwise would be too restrictive.
- What about awaited object
- Should that be unknown?
- Yeah, feels right.
- What about awaited {}?
- {}
- But it could be null or undefined!
- Need to think about this.
- awaited {} -> {} feels okayish, awaited object -> object feels wrong
-
What about a carve-out from objects in expression positions? Things that come from object literals are awaited to themselves, anything else becomes something else.
-
await on Promise<object> already gives you object
- Also, Promise<object> such that the dynamic/runtype type of the object is a Promise can never exist on an A+ Promise - so this is rare in practice.
-
Some other stuff seems broken.
async function a() {
const o: object = Promise.resolve(100);
return o;
}
-
object is so useless - but what is the distinguishing factor between object and Point and Box?
- The latter two are practically thought of as Promises.
-
Wasn't object was always broken? Maybe it doesn't matter what happens here.
- [[Flurry of argument around which is broken: object or {}]]
-
We just need to acknowledge where the type holes are.
-
What about the breaks from awaited T not being assignable to T?
async function foo<T>(x: Promise<T>) {
const y: T = await x; // used to work, errors now.
}
-
Again, is this a necessary break? You can almost never construct a Promise<...> in place of T, so you could never end up with Promise<Promise<T>>.
- But you can write this in the type system.
-
So what are we feeling?
- awaited Promise<object> -> object: yes
- awaited object -> object: No?
- Feels like maybe it should be unknown?
- Why would you write object and intend for it to be Promise? That's practically never the intent.
- So maybe it doesn't matter that much?
-
Joke: what about T extends nonpromise?
- "The opposite of a Promise is a campaign pledge."
-
What about conditionals?
- Is awaited (T extends Foo ? T : Promise<T>) assignable to (T extends Foo ? T : Promise<T>).
-
Conclusion
Optional Chaining and Non-Null Assertions
#36539
- ! in the middle of a chain is a chain, ! at the end is not.
- Seems funny, but appropriate.
Update on CommonJS Emit for Exports
#37093
- Had issues with using getters for export * when a module overrides exports from an export *.
- We were writing getters for properties before they got overwritten.
- Now we set every export ahead of time to undefined so that our __exportStar helper doesn't try to trample over the properties with getters.
Reactions are currently unavailable
Update on Collapsing Intersections of Impossible Discriminated Unions
#36696
Update on awaited
#35998
Looking to reintroduce the awaited type
Held off on this because people wanted to do recursive digging on any type
We had a ton of PR s to fix up Promise.all, Promise.race, etc.
Have one assignability rule that's not sound but is useful: awaited S relates to awaited T if S relates to T
What about a carve-out from objects in expression positions? Things that come from object literals are awaited to themselves, anything else becomes something else.
await on Promise<object> already gives you object
Some other stuff seems broken.
object is so useless - but what is the distinguishing factor between object and Point and Box?
Wasn't object was always broken? Maybe it doesn't matter what happens here.
We just need to acknowledge where the type holes are.
What about the breaks from awaited T not being assignable to T?
Again, is this a necessary break? You can almost never construct a Promise<...> in place of T, so you could never end up with Promise<Promise<T>>.
So what are we feeling?
Joke: what about T extends nonpromise?
What about conditionals?
Conclusion
Optional Chaining and Non-Null Assertions
#36539
Update on CommonJS Emit for Exports
#37093