0

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"

2 Answers 2

2

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!

Playground link to code

Sign up to request clarification or add additional context in comments.

1 Comment

polymorphic this - this is what I need, thank you very much!
0

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 = () => {}
}

7 Comments

This does not fit, because I can do this: new TestA().equals(new TestB) and I won't get an error from typescript
can you explain what will not fit?
It'll fit beacuse when you do new TestA().equals(new TestB) it will return false
I mean that the TS does not report an error in types, but I want to always be sure that I compare TestA with TestA and TestB with TestB
in that case, you have to change the design of interfaces and classes. with current design, you can't achieve that
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.