Skip to content

Polymorphic 'this' type#4910

Merged
ahejlsberg merged 21 commits into
masterfrom
polymorphicThisType
Sep 30, 2015
Merged

Polymorphic 'this' type#4910
ahejlsberg merged 21 commits into
masterfrom
polymorphicThisType

Conversation

@ahejlsberg

Copy link
Copy Markdown
Member

This PR implements polymorphic typing of this for classes and interfaces as inspired by #3694. The new features are:

  • The type of this in an expression within a non-static class or interface member is considered to be an instance of some class that derives from the containing class as opposed to simply an instance of the containing class.
  • The this keyword can be used in a type position within a non-static class or interface member to reference the type of this.
  • When a class or interface is referenced as a type, all occurrences of the this type within the class (including those inherited from base classes) are replaced with the type itself.

This feature makes patterns such as fluent interfaces and covariant return types much easier to express and implement. Languages such as C++, Java, and C# use a somewhat involved generic pattern to emulate this feature, as described here.

An example:

class A {
    foo() {
        return this;
    }
}

class B extends A {
    bar() {
        return this;
    }
}

var b: B;
var x = b.foo().bar();  // Fluent pattern works, type of x is B

In a non-static member of a class or interface, this in a type position refers to the type of this. For example:

class Entity {
    clone(): this {
        // Code to clone Entity
    }
    equals(other: this): boolean {
        // Code to compare Entity with another Entity
    }
}

Each subclass of the above Entity will have a clone method that returns an instance of the subclass, and an equals method that takes another instance of the subclass.

The this type is a subtype of and assignable to the instance type of the containing class or interface, but not vice-versa (because this might actually be a subclass). That is a breaking change, and certain code patterns that previously compiled may now need an extra type annotation:

class A {
    getInstance() {  // Should be getInstance(): A
        return this;
    }
}

class B extends A {
    getInstance() {
        return new B();
    }
}

The example above now errors because the inferred return type of getInstance in A is this and the inferred type of getInstance in B is B, which is not assignable to this. The fix is to add a return type annotation for getInstance in A.

The polymorphic this type is implemented by providing every class and interface with an implied type parameter that is constrained to the containing type itself (except when the compiler can can tell that this is never referenced within the class or interface). That in particular turns a lot of previously non-generic classes into generic equivalents, causing more symbols and types to be created due to generic instantiation. The observed cost in batch compile time ranges from 0% in code that uses no classes to 6-7% in very class-heavy code.

Note that this PR specifically doesn't aim to implement other parts of #3694 such as this type annotations for functions. Those will be covered by other PRs if we choose to implement them.

Loading
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Breaking Change Would introduce errors in existing code

Projects

None yet

Development

Successfully merging this pull request may close these issues.