Imagine creating an interface called addable
, which defines classes with a method __add__
that adds objects of the same class together.
Additionally, consider having a standalone function called add
that takes two addable
objects of the same class and returns their sum.
The initial approach involved using the this
type in the interface:
interface addable<T> {
__add__: (this: T, other: T) => T;
}
An attempt at implementing this was made with a class named Point:
class Point implements addable {
constructor(public x: number, public y: number) { }
__add__(other: Point) {
return new Point(this.x + other.x, this.y + other.y);
}
}
However, this implementation failed with:
Class 'Point' incorrectly implements interface 'addable'.
Types of property 'add' are incompatible. Type '(other: Point) => Point' is not assignable to type '(other: this) => this'.
Type 'Point' is not assignable to type 'this'.
Further investigation revealed that the this
type does not refer to the class itself, leading to the failure of this approach.
To resolve this issue, generics were introduced with a pseudo-parameter this
:
interface addable<T> {
__add__: (this: T, other: T) => T;
}
In this case, defining the class as specified worked. However, when attempting to define the standalone function add
:
const add = <T>(a1: addable<T>, a2: addable<T>) => a1.__add__(a2);
The error received was:
The 'this' context of type 'addable' is not assignable to method's 'this' of type 'T'.
A potential solution proposed utilizing the following interface:
interface Addable<T> {
add: (this: Addable<T>, other: Addable<T>) => Addable<T>;
}
This definition seemed promising. The standalone function would then be:
const add = <T>(a1: Addable<T>, a2: Addable<T>) => a1.add(a2)
Although effective, it was noted that this definition was too lenient and allowed any instances of Addable
, not strictly those belonging to the same class.
The issue persisted even with different classes implementing the interface:
class Point implements Addable<Point> {
constructor(public x: number, public y: number) { }
add(other: Point) {
return new Point(this.x + other.x, this.y + other.y);
}
}
class Num implements Addable<Num> {
constructor(public number: number) { }
add(other: Num) {
return new Num(this.number + other.number);
}
}
const add = <T>(a1: Addable<T>, a2: Addable<T>) => a1.add(a2)
var p1 = new Point(1, 1);
var num1 = new Num(10);
const wrongAdded = p1.add(num1); // correctly fails
const wrongAdded2 = add(p1, num1); // typechecks! why???
The inferred type of add appeared to be <{}>
, indicating a discrepancy that requires further understanding of TypeScript.