Skip to content

Design Meeting Notes, 8/24/2018 #26952

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

Closed
DanielRosenwasser opened this issue Sep 6, 2018 · 0 comments
Closed

Design Meeting Notes, 8/24/2018 #26952

DanielRosenwasser opened this issue Sep 6, 2018 · 0 comments
Labels
Design Notes Notes from our design meetings

Comments

@DanielRosenwasser
Copy link
Member

Call-site inference for implicit anys

This is a demo presentation

  • [[Opens up minimatch]]
  • "This is the codebase you open up to make yourself feel good about JavaScript"
  • Looking at the charSet declaration function
    • We have noImplicitAny on
    • See that it was at some point called with a string, and so we change the parameter type.
    • If we observe a circularity, we fall back to any and report an error.
  • Is this TypeScript too?
    • It works for either JavaScript or TypeScript
  • How bad is it?
    • The graphs are confusing, but it's over 4.5x slower to look at how the function itself is called to figure out the parameter types.
    • If you only look at use-sites of parameters within the bodies of functions, it'x ~1.8x slower.
  • We mostly imagine that this would be a one-time shot tool
  • Could also imagine that we'd improve the performance here, but it's decent
  • Did we look at the tests at all? You'd figure unit tests would be great for figuring out use-site.
    • But that's a problem for stupid tests like "assert that this throws when I pass in a number"
  • But you get 40% more type coverage on Webpack.
    • That's like half the way there.
    • But even that's not worth slowing down the type-checker so dramatically.
    • Seems like we should be leveraging this somehow.

Weird behavior for spreading arrayish types

#26110

  • We now allow rest parameters to have a generic type (T).

    • The requirement has been that that generic type needs to extend an array (e.g. T extends any[]).
  • But you can come up with several things that actually satisfy arrays.

    • Object subtypes:

      interface CoolArray extends Array<string>
      function foo<T extends any[]>(...args: T): T {
          throw 0;
      }
      foo<CoolArray>();
  • Problem: what does it mean to spread in these object subtypes?

    • For these object subtypes, any argument list translated to an array or a tuple won't ever satisfy these types.
  • Solution: if you ever have a type parameter constrained to something other than an Array or ReadonlyArray, and you are trying to relate to a generic rest parameter, you infer a tuple type from the matching argument and try to infer from that.

    • If that inference fails, TypeScript falls back to the constraint and relates against that.
  • Union subtypes:

    function foo<T extends [] | [number, number]>  (...args: T): T {
        throw 0;
    }
    foo<CoolArray>();

Named type arguments, associated types, and partial inference

  • In Named Type Arguments & Partial Type Argument Inference #23696, we realized that making every type parameter nameable would be problematic for existing APIs - it exposes what were thought of as mostly implementation details.

  • What problems were we trying to solve?

    1. Positional type arguments are problematic because it locks you down.
    2. Using a type parameter as a default for a temporary/local type.
      • StrictEventEmitter
      • "Is this a real type!?"
    3. Specialized subclasses:
    ```ts
    class Box<T> { x: T }
    class SpecialBox extends Box<Speical>
    
    // How do I specialize this further without making SpecialBox generic?
    class SpecialSpecialBox extends SpecialBox {
    }
    ```
    
  • At a high level, we want to be able to place types within types.

    declare class BoxObserver<T> {
        type Boxified = { value: T };
        get(): Boxified;
    }
    let x: BoxObserver<number, 
    • Hold up though, that means in this class you can't write the following, since you don't know if Boxified will be further specialized.

      let x: T = this.get().value
  • Feels strange that these can appear in the type parameter list.

  • One of the core differences is that we also don't do inference on these types.

  • Also, these must be associated with the instance.

    • They must be, because they close over the instance type parameters.
  • If changing the type in the subtype changes the behavior in the supertype, then it must be a type variable.

  • But there are merits to just having this as an alias.

    • Could imagine

      declare class Foo<T> with Boxified = { value: T } {
          get(): Boxified;
      }
  • Also, feels strange that there is some mixing of type parameters and aliases:

    type Foo<T> =
        type X = Foo<T>[] in {
            // ...
        };
@DanielRosenwasser DanielRosenwasser added the Design Notes Notes from our design meetings label Sep 6, 2018
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

1 participant