Reputation: 107
I want classes that implement the Observer interface to also implement the Comparable interface, how can I do this?
interface Comparable<T> {
equals: (item: T) => boolean;
}
interface Observer extends Comparable<Observer> {
notify: () => void
}
class TestA implements Observer {
private name = '';
equals = (item: TestA) => {
return this.name === item.name
}
notify = () => {}
}
class TestB implements Observer {
private name = '';
equals = (item: TestB) => {
return this.name === item.name
}
notify = () => {}
}
Error:
TS2416: Property 'equals' in type 'TestA' is not assignable to the same property in base type 'Observer'. Type '(item: TestA) => boolean' is not assignable to type '(item: Observer) => boolean'. Types of parameters 'item' and 'item' are incompatible. Property 'name' is missing in type 'Observer' but required in type 'TestA'.
But, TestA implements the Observer interface why are they not compatible?
Of course, I can write like this:
class TestA implements Observer {
private name = '';
equals = (item: Observer) => {
return this.name === item.name
}
notify = () => {}
}
But then I get such an error, and besides, this is not entirely correct, because I want to compare only objects of this class:
Property 'name' does not exist on type 'Observer'.
How to do it right? "typescript": "^3.9.2"
Upvotes: 0
Views: 403
Reputation: 328152
Have you considered using polymorphic this
instead of generics? Your Comparable
and Observer
would become:
interface Comparable {
equals: (item: this) => boolean;
}
interface Observer extends Comparable {
notify: () => void
}
meaning that object of type X
that extends Comparable
needs to have an equals()
method taking a value of type X
. Note that this implies that Comparable
doesn't act like a normal type in terms of substitutability and inheritance. Generally if you have interface B extends A {...}
then you should be able to use a B
anywhere you require an A
:
interface A {
someMethod(x: A): void;
}
interface B extends A {
someOtherMethod(x: B): void;
}
declare const b: B;
const a: A = b; // okay
But this will not work for Comparable
:
declare const o: Observer;
const c: Comparable = o; // error! equals is incompatible
Anyway, this definition of Comparable
will allow your implementations as-is:
class TestA implements Observer {
private name = '';
equals = (item: TestA) => {
return this.name === item.name
}
notify = () => { }
}
class TestB implements Observer {
private name = '';
equals = (item: TestB) => {
return this.name === item.name
}
notify = () => { }
}
But again, you'll run into issues if you try to treat TestA
or TestB
as an Observer
:
function takeObservers(o1: Observer, o2: Observer) {
o1.equals(o2);
}
takeObservers(new TestA()); // error
So you might decide that you actually don't want to constrain equals()
this way.
Okay, hope that helps; good luck!
Upvotes: 2
Reputation: 6277
Change equals(item: TestA)
and equals(item:TestB)
to equals(item : Observer)
in TestA class and TestB class.
Since Comparable type was given as Observable.
And in equals body you can cast observable object to TestA and compare its name property like below.
in TestA class.
class TestA implements Observer {
private name = '';
equals = (item: Observer) => {
if(item instanceof TestA){
return this.name === (item as TestA).name
}
return false
}
notify = () => {}
}
Upvotes: 0