I have a parent class with two subclasses:
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);
But diff()
method returns a Point, not a ImmutablePoint
// Error: type 'Point' is not assignable to parameter of type 'ImmutablePoint'.
const result: ImmutablePoint = pointA.diff(pointB);
I'm looking for a way to re-define the method signature on the subclass without writing a new implementation, is it possible?
I also tried to make diff()
return value this
but it doesn't work because ImmutablePoint doesn't return this
but a new ImmutablePoint
Playground Link
You can have this
as a return type and you can access the constructor of your current object with this.constructor
. That allows you to work more easily with subclassed 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);
You can make Point
generic:
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
(code in playground)
With the new Default generic type variables feature available you should be able to do something like:
abstract class Point<T extends Point = Point> {
...
}
(haven't tested it yet)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With