- 
                Notifications
    You must be signed in to change notification settings 
- Fork 13.1k
Description
Bug Report
π Search Terms
contravariance bypass inheritance variance annotation
π Version & Regression Information
- This is the behavior in every version I tried, and I reviewed the FAQ for entries about variance.
β― Playground Link
Playground link with relevant code
π» Code
In this example we use contravariance to make Comparator<Animal> assignable to Comparator<Dog> : something than can compare animals can compare dogs.
class Animal { }
class Dog extends Animal {
  wouf = true;
}
class Comparator<in T> {
  public compare(a: T, b: T): boolean {
    return true;
  }
}
class DogComparator extends Comparator<Dog> {
  compareDogs = true;
}
// [ok] an Animal comparator can compare Dogs, so then it is assignable to Comparator<Dog>
const okCompareDogs: Comparator<Dog> = new Comparator<Animal>();
// [error as expected] a Dog comparator cannot compare Animals as it may require specific props of Dog for comparison 
const okCompareAnimals: Comparator<Animal> = new Comparator<Dog>();
// [should be an error] It should be the same here, the fact that DogComparator is a subclass of Comparator<Dog> should not bypass contravariance
// DogComparator which extends Comparator<Dog> is still not a subclass of Comparator<Animal> as Animal is not a subclass of Dog
const nokCompareAnimals: Comparator<Animal> = new DogComparator();π Actual behavior
The following line is accepted by the compiler:
const nokCompareAnimals: Comparator<Animal> = new DogComparator();I think the problem is that as DogComparator is a subclass of Comparator, the compiler only checks for
(a: Dog, b: Dog) => boolean to be assignable to (a: Animal, b: Animal) => void, regardless of contravariance annotation put on the generic type.
π Expected behavior
The nok line above should give a compilation error as contravariance on Comparator's generic parameter should prevent DogComparator (which extends Comparator<Dog>) from being treated as a Comparator<Animal>.
Even if  (a: Dog, b: Dog) => boolean is assignable to (a: Animal, b: Animal) => void, there should be a second contraint to check if Animal is a subtype of Dog (which will give the error) because of contravariance on the generic parameter.
For information this is the case in Kotlin: see this playground sample
Thanks π