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, 1/20/2017 #13607

Closed
DanielRosenwasser opened this issue Jan 20, 2017 · 0 comments
Closed

Design Meeting Notes, 1/20/2017 #13607

DanielRosenwasser opened this issue Jan 20, 2017 · 0 comments
Labels
Design Notes Notes from our design meetings

Comments

@DanielRosenwasser
Copy link
Member

DanielRosenwasser commented Jan 20, 2017

Defaults for generic type parameters (#13487)

  • @rbuckton decided to revisit this.
  • Allows defaults in type parameters in classes, interfaces, function signatures.
  • Rules
    • Required type parameters cannot not follow optional type parameters.
    • Type parameters can't have defaults which circularly reference themselves.
    • Defaults must satisfy their constraints.
    • A class or interface may introduce a default for an existing type parameter - the analogous entities merge.
    • When performing type argument inference, and a type argument with a default lacks any candidates, instead of {}, use the default type.
  • What about the following?
    interface I {
        x: T
    }
    
    interface I<T = number> {
        // ...
    }
    • Currently means that x: T refers to the type parameter.
    • Seems absolutely wrong that that should even occur.
    • Maybe let's just make that an error because it's ambiguous.
  • Current implementation seems to always perform inference, even if type arguments are entirely supplied.
    • Fix that.
  • Why even perform inference if you have a type parameter default?
    • Because inference can do better.
  • What are some motivating use-cases?
    • The overloads for then and catch in Promise which just don't work in subtle ways.
      • We keep trying to patch them up, default type parameters seem to fix this.
    • A React.Component which doesn't use its this.state
      • DR: Is it a hazard if people don't know about type parameter defaults, but do want a typed state?
        • RC: state can still be overridden in the subclass.
          • RC: But then parameter on setState will still be any. :(
            • DR: But then we could have setState's parameter typed as this["state"]! 👍

Resolution: Sounds good so far, let's take a look at the PR!

Mixins and Extending generic type parameters (#4890)

  • People have often asked for the ability to extend a type parameter.

    interface Foo<T> extends T {
        // ...
    }
    • Problem: extends today currently implies that TypeScript will perform some type-checking to see if members in the current type are compatible with those in the derived type.
    • If we can't give errors up-front, we can't give certain guarantees, which would not be good.
    • Currently, the work done does not allow you to extend from type-parameters.
    • However, we allow you to intersect the types (i.e. using the & operator), and the behavior is well-defined for conflicting members (just recursively intersect).
      • This is mostly fine, but we don't allow users to inherit from constructors that return intersection types.
  • New changes

    • Allow clases to inherit from things that construct object types or intersection types.
    • Allow interfaces to extend from both object types and intersection types.
    • Allow classes to implement from both object types and intersection types.
  • These new changes allow users to reconcile difficulties when mixing and matching interfaces which extend, and type aliases which intersect.

    • Great! Less friction.
    • Means you can inherit from Partial<T> and Readonly<T>!
  • Difficulty: intersections previously never adjusted the this type of intersected members.

    interface A {
        x: this;
    }
    
    interface B {
        y: this;
    }
    
    // Expected to work, previously an error.
    declare let foo: A & B;
    a.x.y;
    x.y.x
    • We now instantiate the this type with the intersection itself.
      • This is great - mixins can actually be modeled more easily because the this type can be carried onward.
    // Something constructable.
    type Constructor<T> = new (...args: any[]) => T;
    
    // Strip out signatures from types.
    type Props<T> = { [P in keyof T]: T[P] };
    
    declare function Mixin<B, BC, M, MC>(base: Constructor<B> & BC, mixin: Constructor<M> & MC): Constructor<B & C> & Props<BC & MC>;
  • [[Too many samples to effectively jot notes...]]

  • So mixins could be sort of modeled, but there's still some weird stuff that needs to be handled with construct signatures.

    • Effectively would be magic from the perspective of the user.
    • Basically want to inherit the construct list from the base, and potentially tack on any required parameters in the derived constructor.
    function identifiable<S extends new (...args: any[]) => any)>(superClass: S) {
        return class extends superClass {
            _id: string;
            static superStatic: string;
            constructor(id, ...args) {
                super(...args);
                this._id = id;
            }
        }
    }
    • Basically rephrased: what is the type of args that type-checks? What construct signatures are produced on the new class?
    • We can think of this down the line.
@DanielRosenwasser DanielRosenwasser added the Design Notes Notes from our design meetings label Jan 20, 2017
@microsoft microsoft locked and limited conversation to collaborators Jun 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Design Notes Notes from our design meetings
Projects
None yet
Development

No branches or pull requests

1 participant