Skip to content

Method override error with generic this- only for non-overloaded methods. #23960

Closed
@zamb3zi

Description

@zamb3zi

TypeScript Version: 2.8.3

Search Terms: generic this, override, polymorphic this

Code

namespace N1 {
    /*
        The following method override generates:
        Property 'f' in type 'D' is not assignable to the same property in base type 'C'.
        Type '<T extends D>(this: T, arg: keyof T) => void' is not assignable to type '<T extends C>(this: T, arg: keyof T) => void'.
            The 'this' types of each signature are incompatible.
            Type 'T' is not assignable to type 'D'.
                Type 'C' is not assignable to type 'D'.
                Property 'y' is missing in type 'C'.
    
        Why is generic T treated differently to polymorphic this?
    */
    class C {
        x: number;
        f<T extends C>(this: T, arg: keyof T) { }
    }

    class D extends C {
        y: string;
        f<T extends D>(this: T, arg: keyof T) { }
    }
}

namespace N2 {
    /*
        By adding a pointless overload the method override works fine.
    */
    class C {
        x: number;
        f<T extends C>(this: T, arg: keyof T): void;
        f<T extends C>(this: T, arg: keyof T): void;
        f<T extends C>(this: T, arg: keyof T) { }
    }

    class D extends C {
        y: string;
        f<T extends D>(this: T, arg: keyof T) { }
    }
}

namespace N3 {
    /*
        The problem occurs similarly with static methods where polymorphic this is
        not available. This time the error occurs on the definition of D however.
        As before, overloading works around the issue.
    */
    type Constructor<T> = { new(...args: any[]): T };
    class C {
        x: number;
        static c: boolean;
        // static f<T extends C>(this: Constructor<T>, arg: keyof T): void;
        // static f<T extends C>(this: Constructor<T>, arg: keyof T): void;
        static f<T extends C>(this: Constructor<T>, arg: keyof T) { }
    }

    class D extends C {
        y: string;
        static d: boolean;
        static f<T extends D>(this: Constructor<T>, arg: keyof T) { }
    }
}

namespace N4 {
    /*
        This is another workaround using T extends C in the sub-class but still contraining this
        to be a D class or subclass.
    */
    type Constructor<T> = { new(...args: any[]): T };
    class C {
        x: number;
        static c: boolean;
        static f<T extends C>(this: typeof C & Constructor<T>, arg: keyof T) { }
    }

    class D extends C {
        y: string;
        static d: boolean;
        static f<T extends C>(this: typeof D & Constructor<T & D>, arg: keyof T) { }
    }
}

Expected behavior:

Non-static case: behavior should be the same, with and without use of overloading. It seems as if the overloaded behavior is correct. T should be treated as if it were polymorphic this.

Static case: this is more of a problem because this pattern seems to be the current best practice for achieving static polymorphic this (see: [#5863]). Both of the workarounds ought not be needed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Working as IntendedThe behavior described is the intended behavior; this is not a bug

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions