Description
Update on Collapsing Intersections of Impossible Discriminated Unions
- Turns impossible intersections into
never
s - 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 addundefined
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?
- Yes.
- Things to keep in mind:
IteratorResult
usesundefined
as a discriminant
- When you're not in
- 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
-
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 toawaited T
ifS
relates toT
- 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
orundefined
!
- Should that be
- Need to think about this.
awaited {} -> {}
feels okayish,awaited object -> object
feels wrong
- Not sound because subtypes might introduce a
-
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
onPromise<object>
already gives youobject
- Also,
Promise<object>
such that the dynamic/runtype type of theobject
is aPromise
can never exist on an A+ Promise - so this is rare in practice.
- Also,
-
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 betweenobject
andPoint
andBox
?- 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{}
]]
- [[Flurry of argument around which is broken:
-
We just need to acknowledge where the type holes are.
-
What about the breaks from
awaited T
not being assignable toT
?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 ofT
, so you could never end up withPromise<Promise<T>>
.- But you can write this in the type system.
-
So what are we feeling?
awaited Promise<object>
->object
: yesawaited object
->object
: No?- Feels like maybe it should be
unknown
? - Why would you write
object
and intend for it to bePromise
? That's practically never the intent.- So maybe it doesn't matter that much?
- Feels like maybe it should be
-
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>)
.
- Is
-
Conclusion
- Iterate
- @rbuckton to file a bug on
downlevel-dts
Optional Chaining and Non-Null Assertions
!
in the middle of a chain is a chain,!
at the end is not.- Seems funny, but appropriate.
Update on CommonJS Emit for Exports
- Had issues with using getters for
export *
when a module overrides exports from anexport *
.- 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.