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

Design Meeting Notes, 2/28/2020 #37115

Closed
DanielRosenwasser opened this issue Feb 28, 2020 · 1 comment
Closed

Design Meeting Notes, 2/28/2020 #37115

DanielRosenwasser opened this issue Feb 28, 2020 · 1 comment
Labels
Design Notes Notes from our design meetings

Comments

@DanielRosenwasser
Copy link
Member

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.
@DanielRosenwasser DanielRosenwasser added the Design Notes Notes from our design meetings label Feb 28, 2020
@jablko
Copy link
Contributor

jablko commented Mar 3, 2020

  • We had a ton of PR s to fix up Promise.all, Promise.race, etc.

    • All of them broken in different wonderful ways.

The objection to #33707 is that it doesn't model the recursive behavior of await with respect to Promise<Promise<number>>.

Promise<Promise<number>> isn't valid: Neither native promises nor A+-compliant 3rd-party promises can be nested.

Promise<Promise<number>> can and does happen accidentally. #35998 accommodates these accidents in more but not all contexts: Full support would mean making Promise<Promise<number>> assignable to Promise<number> which is an even bigger change than adding a new kind of type? Meanwhile better partial support will encourage more bugs like this, because they are tolerated in more but not all contexts.

I propose consistently not supporting nested i.e. non-A+-compliant promises.

An advantage of not supporting nested promises is that it doesn't require a new kind of type.

The reason to introduce a new kind of type, besides accommodating accidentally nested promises, is to support non-A+-compliant promises as in jQuery 2. I have very little context but haven't seen any demand for better support for non-A+-compliant promises. I'm skeptical that adding a new kind of type for this is justified?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Design Notes Notes from our design meetings
Projects
None yet
Development

No branches or pull requests

3 participants