Typescript: Hur man sätter metodens returvärde => underklass


Jag har en basklass med två underklasser:

abstract class Point {
    public readonly x: number;
    public readonly y: number;

    constructor(x: number, y: number) {
        this.x = x;
        this.y = y;
    }

    diff(point: Point): Point {
        return this.update(this.x - point.x, this.y - point.y);
    }
    // many methods like diff(); and then...

    protected abstract update(x: number, y: number): Point;
}



class ImmutablePoint extends Point {
    protected update(x: number, y: number): Point {
        return new ImmutablePoint(x, y);
    }
}



class MutablePoint extends Point {
    public x: number;
    public y: number;

    protected update(x: number, y: number): Point {
        this.x = x;
        this.y = y;
        return this;
    }
}


const pointA: ImmutablePoint = new ImmutablePoint(10, 10)
const pointB: ImmutablePoint = new ImmutablePoint(6, 2);

Men diff()-metoden returnerar en Point, inte en ImmutablePoint

// Error: type 'Point' is not assignable to parameter of type 'ImmutablePoint'.
const result: ImmutablePoint = pointA.diff(pointB);

Jag letar efter ett sätt att omdefiniera metodsignaturen i underklassen utan att skriva en ny implementation, är det möjligt?

Jag försökte också få diff() att returnera värdet this men det fungerar inte eftersom ImmutablePoint inte returnerar this utan en ny ImmutablePoint

Lekplatslänk

Svar #1

Du kan ha this som returtyp och du kan komma åt konstruktören för ditt nuvarande objekt med this.constructor. Det gör att du kan arbeta lättare med subklassade immutables.

abstract class Point {
    public readonly x: number;
    public readonly y: number;

    constructor(x: number, y: number) {
        this.x = x;
        this.y = y;
    }

    diff(point: Point): this {
        return this.update(this.x - point.x, this.y - point.y);
    }
    // many methods like diff(); and then...

    protected abstract update(x: number, y: number): this;
}



class ImmutablePoint extends Point {
    protected update(x: number, y: number): this {
        return new (<any>this.constructor)(x, y);
    }
}



class MutablePoint extends Point {
    public x: number;
    public y: number;

    protected update(x: number, y: number): this {
        this.x = x;
        this.y = y;
        return this;
    }
}


const pointA: ImmutablePoint = new ImmutablePoint(10, 10)
const pointB: ImmutablePoint = new ImmutablePoint(6, 2);
const result: ImmutablePoint = pointA.diff(pointB);

Svar #2

Du kan göra Point generisk:

abstract class Point<T extends Point<any>> {
    public readonly x: number;
    public readonly y: number;

    constructor(x: number, y: number) {
        ...
    }

    diff(point: Point<any>): T {
        return this.update(this.x - point.x, this.y - point.y);
    }

    protected abstract update(x: number, y: number): T;
}

class ImmutablePoint extends Point<ImmutablePoint> {
    protected update(x: number, y: number): ImmutablePoint {
        return new ImmutablePoint(x, y);
    }
}

class MutablePoint extends Point<MutablePoint> {
    public x: number;
    public y: number;

    protected update(x: number, y: number): MutablePoint {
        ...
        return this;
    }
}


const pointA: ImmutablePoint = new ImmutablePoint(10, 10)
const pointB: ImmutablePoint = new ImmutablePoint(6, 2);
const result: ImmutablePoint = pointA.diff(pointB); // fine

(kod i lekplats)

Med den nya funktionen för standard generiska typvariabler tillgänglig borde du kunna göra något som:

abstract class Point<T extends Point = Point> {
  ...
}

(har inte testat det än)

Relaterade artiklar