Skip to content

Feature suggestion: treat prototype assignments like merging type declarations #4038

Closed

Description

I would like better native support for code of this nature:

function impl1() { // implementation A for myVirtualMethod
    this.x = 1; // this is treated as global object or any
}

function impl2() { // implementation B for myVirtualMethod
    this.y = 1; // no type-safety, compiler doesn't know what 'this' will be bound to.
}

export class Base {
    public myVirtualMethod() {}

    public x: number;
}

export class Derived1 extends Base {
    // can't assign function to myVirtualMethod here to override method
}
Derived1.prototype.myVirtualMethod = impl1; // override method with function

export class Derived2 extends Base {}
Derived2.prototype.myVirtualMethod = impl2; // override method with function

The code above works, but has a couple of drawbacks, which I think are resolvable:
The compiler has no way of knowing what this pointer should be in impl1 and impl2

  • I would like a way to redefine what type this is for functions.
  • It would also be nice if the compiler could detect if this contract is not honored (e.g., the function is invoked without a receiver, or is assigned to a method on a class which does not match the interface)
  • Possibly infer an interface for the receiver of the function if none is declared

Possible syntax:
function impl1() overrides Base.myVirtualMethod {}
function impl1(): this implements Base {}
function impl1() { const this: Base; }

The compiler won't let you define a method by assigning a function, so you must assign to the prototype instead

  • It may be nice to have a way to designate that certain properties are intended to be assigned to the prototype, from inside the class body
  • I would like way to declare a method but define the body through assignment

Possible syntax:
public myVirtualMethod() = impl1; // Parentheses distinguish this from a property assignment.
public myVirtualMethod() = null; // pure virtual method (runtime error to invoke, no compile time check)

Type checking:
impl1(); // Type error: Window does not implement { x: number }
this.y = 1; // Type error: Base does not implement { y: number }
public myVirtualMethod() = impl2; // Type error: Derived1 does not implement { y: number }
impl2.call( new Derived1 ); // Type error: Derived1 does not implement { y: number }

Activity

  1. kitsonk commented on Jul 27, 2015

    @kitsonk
    Contributor

    Sounds like a combination of several dupes: #229 #3694 #3442

  2. kitsonk commented on Jul 27, 2015

    @kitsonk
    Contributor

    Ah, I see, you then are looking for mixins/traits (#311) or some sort of abstract class (#6, #2947). The way you are doing it, by directly modifying the prototype will mean the current scope is always a bit of a challenge.

  3. danquirk commented on Jul 28, 2015

    @danquirk
    Member

    The request here is to essentially make directly assigning to .prototype analogous to an actual type declaration as far as type checking is concerned.

  4. added
    SuggestionAn idea for TypeScript
    Needs ProposalThis issue needs a plan that clarifies the finer details of how it could be implemented.
    on Jul 28, 2015
  5. changed the title [-]Feature suggestion: method override by function[/-] [+]Feature suggestion: treat prototype assignments like merging type declarations[/+] on Jul 28, 2015
  6. kitsonk commented on Jul 29, 2015

    @kitsonk
    Contributor

    In theory, if it is reasonably achievable in typescript, it solves some of the issues around "natively" supporting Mixins and Traits (as well as Decorators would modify the type declaration, which they don't currently).

    Dan Quirk (@danquirk) do you think a reasonable proposal has a chance?

  7. danquirk commented on Jul 29, 2015

    @danquirk
    Member

    Kitson Kelly (@kitsonk) I haven't really chatted with anyone else on the team about this in depth yet so I don't want to make any promises I can't keep :)

    At first glance this is definitely a bit outside what we normally do as far as flow control based semantics and such but it does serve a real use that many people would benefit from. I wonder how much ES6 classes will eventually obviate the need for direct prototype assignment in new code (obviously that still leaves a ton of existing JS that could benefit from this). Some might argue this sort of code is on the more dynamic side of the world which we generally try to allow but don't always support incredibly well (ex dynamic assignment/expandos typed as any).

  8. RyanCavanaugh commented on Jul 29, 2015

    @RyanCavanaugh
    Member

    On the other hand, the work we're doing in the JS intellisense branch to pick up define and require calls (in non-TS code) might set a good precedent for how these things might work in the future.

  9. kitsonk commented on Jul 30, 2015

    @kitsonk
    Contributor

    I wonder how much ES6 classes will eventually obviate the need for direct prototype assignment in new code (obviously that still leaves a ton of existing JS that could benefit from this).

    Dan Quirk (@danquirk) yes, but you might assuming that ES Classes will support the right sort of design patterns. I for one remain unconvinced that ES Classes will head the direction everyone wants to go, and even then, it might take a while to get something agreed.

    I know TypeScript is trying to do its best to be "future proof" and not meddle in things that might break in the future, but I suspect having a mechanism of extending the typing of a prototype won't interfere with future standards and get a lot of us who don't believe traditional OO inheritance is the right pattern off your backs. 😉 If we wanted to do composition, or traits, factories, or whatever, we could be the "Wizard of Oz" behind the curtains to ensure that our downstream consumed code was type safe.

  10. danquirk commented on Jul 30, 2015

    @danquirk
    Member

    Right, my concern here re:ES6 was not really future incompatibility and just musing on the general ROI of any work here if we expect people to move to ES6 classes in short order. There's definitely some opportunity here for TypeScript to just infer the right thing from a pattern that people are already commonly using, although as far as things like traits and mixins I'd prefer something a little more explicit/declarative than a mess of prototype assignments magicing up a type somewhere.

  11. kitsonk commented on Jul 31, 2015

    @kitsonk
    Contributor

    Dan Quirk (@danquirk) I would too, but #311 is closed. 😢 The main reason was, if I understand correctly "we are going to wait for ECMAScript to take the lead..." It is hard to wait for TC39 to try to use and deliver other design patterns.

    But if this sort of "modifying the prototype type" were allowed, I, as a middle man, could produce some of this functionality... I will admit Bryan Forbes (@bryanforbes) and I have been doing a lot of thinking about composition, factories, traits and mixins for Dojo 2. While union and intersection types will help us out, being able to modify the type of the prototype, would make it challenging for us, but we would be able to deliver a clean API that allowed use of the pattern without breaking TypeScript.

  12. RyanCavanaugh commented on Jul 17, 2020

    @RyanCavanaugh
    Member

    We've implemented this for JavaScript, but don't see a need to do it for modern TypeScript code. Many better alternatives exist

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Needs ProposalThis issue needs a plan that clarifies the finer details of how it could be implemented.SuggestionAn idea for TypeScript

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions