Skip to content

Allow arbitrary expressions in 'extends' clauses #511

Closed
@RyanCavanaugh

Description

@RyanCavanaugh

The language currently only allows dotted identifiers in extends clauses, e.g. class Foo extends X.Y.Bar { }. The identifier must resolve to a class in both the value and type namespaces.

With the advent of class expressions (#497), we'll presumably need to start allowing arbitrary expressions, or at least loosen up the restriction that the named parent be an actual class.

var B: /* ... */;
class A extends B { }

The question is, given the type of B, how do we determine the type of A? Any rule should ideally end up at the same result as the current type system when B is a class.

Construct signatures?

Rule
The simplest rule would be that the return type of the construct signature of B is the prototypal parent of A. The constructor of A must invoke that construct signature (via super) according to the same rules as usual.

Issues
Problematically, B can actually have any number of construct signatures, with no restriction:

interface Evil {
  new(n: string): Horse;
  new(n: number): Airplane;
}
var B: Evil;
// What is A??
class A extends B { }

We cannot use A's super call to resolve the ambiguity via overload resolution because A might be an ambient class, in which case we won't know which super overload was selected.

Prototype?

Rule
TypeScript defines the prototype property of a class constructor function to be the same as the instance shape of the class. This is the prototypal parent of A, and A's constructor must call one of B's construct signatures via super.

Issues
The prototype property replaces all references to A's type parameters with any, erasing the genericness of the type.

Decision points

Questions to consider at this point:

  • Do base class expressions really need to have a prototype property? Probably not.
  • Are types with random construct signatures realistic base types? Probably not.

Compromise

Rule
A class extending some type B has a prototypal parent of the best common type of the return types of B's construct signatures. This best common type must exist (not {}), and all of B's construct signatures must have same number of generic type parameters. The extending class must specify that same number of generic type arguments when referencing B in the extends clause, which are then applied to the BCT of B's construct signature. Otherwise, it is an error to attempt to extend from B.

If a super call is required, the extending type's constructor may invoke any construct signature of the base type.

TBD

  • How does static inheritance work here?

Metadata

Metadata

Assignees

No one assigned

    Labels

    ES6Relates to the ES6 SpecFixedA PR has been merged for this issueSuggestionAn idea for TypeScript

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions