Skip to content

Design Meeting Notes, 2/28/2020 #37115

Closed
Closed
@DanielRosenwasser

Description

@DanielRosenwasser

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?
      • Yes.
    • 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

    • Iterate
    • @rbuckton to file a bug on downlevel-dts

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Design NotesNotes from our design meetings

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions