Polymorfisk this i TypeScript


Jeg prøver å gjøre det jeg tror er et «lærebok»-tilfelle for polymorfisk this, og det fungerer ikke, og jeg får det bare ikke til å gå opp. Tenk deg at jeg har en abstract-basisklasse som kan klones, f.eks.:

abstract class X {
    abstract clone(): this;
}

Nå vil jeg implementere en baseklasse som gir en clone-implementering:

class Y extends X {
    constructor(private x: number) { super() }
    clone(): this { return new Y(this.x + 1); }
}

Når jeg gjør dette, får jeg en feil som sier Type Y is not assignable to type 'this'. Jeg er helt forvirret. Alt jeg ønsker å formidle her er typebegrensningen om at hvis en underklasse av X får sin clone-metode kalt, vil typen av det du får tilbake være identisk med subtypen. Er ikke dette akkurat det polymorfiske this er til for? Hva gjør jeg galt her?

Her er en lenke til denne koden i TypeScript-lekeplassen.

Svar #1

Jeg har merket Artems svar som det riktige fordi han ser ut til å ha rett i at det virkelig ikke er mulig å gjøre dette på en 100 % sikker måte.

Det finnes imidlertid en måte å få kompilatoren til å håndheve typebegrensningen jeg ønsket. Så jeg bestemte meg for å inkludere dette svaret i tilfelle det er nyttig for folk. Den eneste ulempen med min tilnærming er at den er litt usikker av akkurat de grunnene som Artem påpeker, altså at noen kan utvide klassene dine uten å gi en implementering av clone og skape en situasjon der den returnerte verdien egentlig ikke ville være det du hevder den er.

Min løsning var å bare legge til en typekonvertering. Igjen, dette er generelt usikkert. Men hvis du aldri utvider fra klassene, fungerer det fint så vidt jeg kan se. Så min løsning var:

class Y extends X {
    constructor(private x: number) { super() }
    clone(): this {
        return new Y(this.x + 1) as this;
    }
}

Du kan se en komplett versjon som kompilerer her.

Svar #2

Nei, slik situasjonen er akkurat nå, er det ikke en støttet brukstilfelle for denne typen. En metode med this returtype bør returnere en instans av den avledede klassen selv om denne metoden ikke er overstyrt i den avledede klassen - se koden i dette svaret for et eksempel som begrunner dette.

Med andre ord, gitt abstract clone(): this deklarasjon, må denne koden være gyldig selv om Z ikke overstyrer clone selv, men din implementering av clone i Y bryter den.

class Z extends Y {
    f() {
        let c: Z = this.clone();
    }
}

Så det ser ut til at en metode med this returtype alltid bør returnere this.

OPPDATERING: Jeg fant en åpen sak med dette brukstilfellet på GitHub, merket som 'aksepterer pull-forespørsler'. Jeg er imidlertid ikke sikker på om de virkelig har til hensikt å støtte det, eller planlegger å fikse cloneNode i kompilatoren på en annen måte.

Relaterte artikler