Skip to content

Design Meeting Notes, 1/20/2017 #13607

Closed
Closed
@DanielRosenwasser

Description

@DanielRosenwasser

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.

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