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

Polymorphic this with subclassing #17533

Closed
cletusw opened this issue Jul 31, 2017 · 9 comments
Closed

Polymorphic this with subclassing #17533

cletusw opened this issue Jul 31, 2017 · 9 comments
Labels
Duplicate An existing issue was already created

Comments

@cletusw
Copy link

cletusw commented Jul 31, 2017

Using Polymorphic "this" for static members workaround from #5863 no longer works with subclassing in TypeScript 2.4.

TypeScript Version: 2.4.2

Code

class Parent {
    static create<T extends Parent>(this: { new (): T }): T {
        return new this();
    }
}

class Child extends Parent {
    static create(): Child {
        return super.create<Child>();
    }
}

Expected behavior:
Should not have type errors, which was the behavior in 2.3.4.

Actual behavior:

test.ts(7,7): error TS2417: Class static side 'typeof Child' incorrectly extends base class static side 'typeof Parent'.
  Types of property 'create' are incompatible.
    Type '() => Child' is not assignable to type '<T extends Parent>(this: new () => T) => T'.
      Type 'Child' is not assignable to type 'T'.
@cletusw
Copy link
Author

cletusw commented Jul 31, 2017

May be related to #16790 and/or #5863 (comment)

@gcnew
Copy link
Contributor

gcnew commented Jul 31, 2017

Unfortunately this pattern has been incorrect but undetected. See #17509 (comment).

@DanielRosenwasser DanielRosenwasser added the Duplicate An existing issue was already created label Jul 31, 2017
@DanielRosenwasser
Copy link
Member

Why are you overriding the definition anyway?

@cletusw
Copy link
Author

cletusw commented Jul 31, 2017

In my use case, create has a boolean flag parameter that needs to have a different default value in the two implementations. I just elided that for this minimum repro case.

@DanielRosenwasser
Copy link
Member

Gotcha - you can be more explicit about the type parameter and just use a type assertion to tell the type system that you know better in this case, but it isn't quite as safe I suppose.

    class Parent {
        static create<T extends Parent>(this: { new (): T }): T {
            return new this();
        }
    }

    class Child extends Parent {
        static create<T extends Child>(this: { new(): T}): T {
            return super.create() as T;
        }
    }

@cletusw
Copy link
Author

cletusw commented Aug 1, 2017

@DanielRosenwasser Thanks! That does work, until I add a property to the child class that doesn't exist on the parent:

class Parent {
    static create<T extends Parent>(this: { new (): T }): T {
        return new this();
    }
}

class Child extends Parent {
    childProperty: string;
    static create<T extends Child>(this: { new (): T }): T {
        return super.create() as T;
    }
}
test.ts(7,7): error TS2417: Class static side 'typeof Child' incorrectly extends base class static side 'typeof Parent'.
  Types of property 'create' are incompatible.
    Type '<T extends Child>(this: new () => T) => T' is not assignable to type '<T extends Parent>(this: new () => T) => T'.
      The 'this' types of each signature are incompatible.
        Type 'new () => T' is not assignable to type 'new () => Child'.
          Type 'T' is not assignable to type 'Child'.
            Type 'Parent' is not assignable to type 'Child'.
              Property 'childProperty' is missing in type 'Parent'.

Any ideas?

(Also, if this needs to be closed and moved to a Stack Overflow or something, just let me know.)

@mhegazy
Copy link
Contributor

mhegazy commented Aug 17, 2017

Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.

@mhegazy mhegazy closed this as completed Aug 17, 2017
@zamb3zi
Copy link

zamb3zi commented May 10, 2018

This works and also provides access to any static members.

type Constructor<T> = { new(): T };

class Parent {
    a = 7;
    static b = true;
    static create<T extends Parent>(this: typeof Parent & Constructor<T>) {
        return new this();
    }
}

class Child extends Parent {
    y = 42;
    static z = 'foo';
    static create<T extends Parent>(this: typeof Child & Constructor<T & Child>) {
        this.z; (new this).y;
        return super.create() as T;
    }
}

See [#23960].

@cletusw
Copy link
Author

cletusw commented May 10, 2018

@zamb3zi I don't have my original use case any more, but that looks pretty good, thanks!

@microsoft microsoft locked and limited conversation to collaborators Jul 31, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

5 participants