There are two potential solutions to this problem, but each comes with its own set of drawbacks.
The first approach involves utilizing the polymorphic this
in the interface. This means that the parameter type will be determined by the implementer:
interface I {
test: (a: this) => boolean;
}
class Test implements I {
constructor (public additional: number) {}
test (a: Test) { return false; }
}
let o:I = new Test(1); // error
Playground Link
One drawback of this method is the loss of assignability to the interface. However, this may not be a concern depending on your specific needs.
The alternative option is to use a method signature in the interface instead of a function signature. Method signatures are checked bivariantly, which may lead to less type safety. Yet, implementing the class becomes easier without losing assignability to the I
interface:
interface I {
test(a: I): boolean;
}
class Test implements I {
constructor (public additional: number) {}
test (a: Test) { return this.additional === a.additional; }
}
class Test2 implements I {
constructor (public additional2: number) {}
test (a: Test2) { return this.additional2 === a.additional2; }
}
let o:I = new Test(1);
o.test(new Test2(1)) // access additional when Test2 only has additional2
Playground Link