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, 6/12/2020 #39054

Closed
DanielRosenwasser opened this issue Jun 12, 2020 · 8 comments
Closed

Design Meeting Notes, 6/12/2020 #39054

DanielRosenwasser opened this issue Jun 12, 2020 · 8 comments
Labels
Design Notes Notes from our design meetings

Comments

@DanielRosenwasser
Copy link
Member

Custom JSX Fragment Factories

#38720

  • We didn't give a way to specify the factory for fragments the same way we do for regular tags

  • Fragments are a little unique in that there is no tag name. It's just assumed to be React.Fragment

  • Wait, what is a fragment?

    import React from "react"
    
    const a = ()=> (
    
      <> // <- I am a fragment
        <div />
      </>
    
    )
  • We didn't know at the time how fragments were going to be used.

    • Held off because Mithril fragments don't work that way.
    • Still, there's a relatively well-known semantics at this point, Babel support sets precedent
  • Conclusion: Approve pending another review.

Use declare class when Generating Types

microsoft/TypeScript-DOM-lib-generator#858
#39044

  • Is there a reason why?
    • It's cleaner
    • It saves 2.5K LoC
  • But they're not really classes
    • Subtle differences
      • Lexical conflicts
      • globalThis
  • Can just do ambient class merging
    • What? That's not a thing we allow.
  • Can you even merge statics?
    • Only with namespace merging.
    • Doesn't technically allow numeric/quoted/symbol-named properties.
  • Now doesn't allow some of those special names because of static names.
  • There's at least one upside...but we want to make sure it doesn't break anything.
  • Kind of not a fan: they're not really classes, they have issues with merging unles you use namespaces which we're not really fans of, so if we wanted to enable static side merging, we'd prefer interfaces to enable that.
    • But then we have to give them names?
    • Yes, even for the ones we don't care about.
  • Conclusion: relay feedback, consider using interfaces for the static sides.

Node Factory PR

#35282

  • File size change of tsc.js started is about 87K of additional content.
    • Trimming now.
  • There's a lot of node factory functions that do more work than the parser does.
    • e.g. auto-parenthesization logic.
    • Deprecated the functions that already do this.
    • Now one object of factories.
  • You now need to be able to pass factories around - is that a breaking change?
    • Well that's why existing factory functions are being deprecated.
    • Logging warnings - see src/compat/deprecations.ts
  • Does this improve performance?
    • A little bit!
    • Why is parse time higher?
      • More work from bind time moved into parse
  • Some other benefits about being able to reinterpret nodes.
    • parsing top level await after the fact.

Higher Order Tuple Spreads

#5453

  • Some updates on changes in tuple spreads.
  • Consider a type like [string, ...T, number]
    • When T is a tuple with a fixed length ([boolean, boolean]), this just flattens to a fixed tuple. ([string, boolean, boolean, number]).
    • When T has an open length (boolean[]), the rest element is created at the end: ([string, ...(boolean | number)[]])
    • When it's generic, it sticks around: [string, ...T, number]
      • Even for [...T] - important because T might be constrainted to a readonly array (e.g. T extends readonly any[]).
  • Now, JavaScript only allows one rest parameter...
    • BUT, with this, you can have a rest parameter whose type is a tuple with multiple rest positions.

    • Can represent a function that takes numbers at both the beginning and the end.

      declare function foo<T>(x: number, ...rest: [...T[], number])
      
      foo(1, "hello", 2); // works
      foo(1, "hello", false); // error!
  • Sure would be nice if we could have a declaration for bind.
  • One other thing: we always get a question like "if I have a type parameter constrained to a readonly array, why don't I get tuples?"
    • Now we do that.
    • Already can do this with function foo<T extends unknown[]>(x: [] | T)
    • Gotta make sure it comes out as readonly.
      • Could do readonly [...T] instead.
@DanielRosenwasser DanielRosenwasser added the Design Notes Notes from our design meetings label Jun 12, 2020
@treybrisbane
Copy link

treybrisbane commented Jun 13, 2020

@DanielRosenwasser Just out of curiosity, why does [string, ...boolean[], number] result in [string, ...(boolean | number)[]], rather than just staying as [string, ...boolean[], number]? Is it an issue of complexity, or...?

I recall working with APIs in the past that would benefit from the ability to type things like "any number of strings, followed by exactly one function".

@treybrisbane
Copy link

Also, with this new work on type-level tuple transformations, I suspect #27995 will come up more often... 🤔 Time will tell I guess.

@saschanaz
Copy link
Contributor

saschanaz commented Jun 15, 2020

(From microsoft/TypeScript-DOM-lib-generator#858 (comment))

Kind of not a fan: they're not really classes

Could this be elaborated more?

they have issues with merging unles you use namespaces which we're not really fans of

Should a way be introduced? Merging static side of class sounds like a general language issue.

so if we wanted to enable static side merging, we'd prefer interfaces to enable that.

As far as I remember, I suggested same in the past and it was rejected because it would introduce tons of unnecessary names to the global namespace. It means if you type EventHandler you'll get a suggestion for EventHandlerStatic (or whatever) and this will happen for every interface name.

@DanielRosenwasser
Copy link
Member Author

@treybrisbane we could in theory type that when assigning from a fixed tuple to that type, but then it ceases to be useful; for example, there's no (clean) way to know where to grab the last element from. @ahejlsberg can answer the capabilities a bit better since he's working on the PR.

@DanielRosenwasser
Copy link
Member Author

@saschanaz (CC @sandersn)

Could this be elaborated more?

From my understanding, they're not declared as if they were classes. For example, I can define another class AbortController {} and not get an error. On the other hand, maybe it doesn't matter because they seem to be non-enumerable and neither behavior models that well.

@saschanaz
Copy link
Contributor

For example, I can define another class AbortController {} and not get an error.

That's true, although I think the specific behavior doesn't affect TS users as it's an error in TS. Or does it?

@ahejlsberg
Copy link
Member

@treybrisbane With the PR I'm working on you'll be able to model a function taking any number of strings followed by a function as

declare function foo<T extends string[]>(...args: [...T, () => void]): T;

This will indeed infer appropriate strings-only tuple types for T:

foo(() => {});  // []
foo('hello', 'world', () => {});  // [string, string]
foo('hello', 42, () => {});  // Error, number not assignable to string

The one kind of type you won't be able to represent is something like [...A[], ...B[]]. This will instead normalize to [...(A | B)[]. The issue here is that when A and B are not mutually exclusive types (i.e. when one is a subtype of the other), it becomes hard to discern when one sequence ends and the other starts, which in turn means that type relationships become complex (and expensive) to reason about.

@treybrisbane
Copy link

@ahejlsberg Makes sense. Thanks for the explanation! 🙂

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

5 participants