Skip to content

Class inheritance should not bypass generic parameter contravarianceΒ #53798

Closed
@Feavy

Description

@Feavy

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 πŸ˜ƒ

Metadata

Metadata

Assignees

No one assigned

    Labels

    Working as IntendedThe behavior described is the intended behavior; this is not a bug

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions