microsoft/TypeScript

Variance error not caught by the type system

jyuhuan opened this issue · 3 comments

TypeScript Version: 2.1.1

Code

interface Comparator<T> {
  compare(x: T, y: T): number
}

class Animal {
  getAge(): number { return 10; }
}
class Cat extends Animal {
  getClawLength(): number { return 20; }
}

class AnimalComparator implements Comparator<Animal> {
  compare(x: Animal, y: Animal): number {
    return x.getAge() - y.getAge();
  }
}

class CatComparator implements Comparator<Cat> {
  compare(x: Cat, y: Cat): number {
    return x.getClawLength() - y.getClawLength();
  }
}

function sortAnimals(animals: Animal[], comparator: Comparator<Animal>): void {
  // Irrelevant code ...
  comparator.compare(animals[0], animals[1]);
  // Irrelevant code ...
}

const animals = [ new Animal(), new Animal(), new Animal() ]
sortAnimals(animals, new CatComparator())

Expected behavior:
A type error containing the following message should be reported by the compiler:

Type 'Comparator<Cat>' is not assignable to type 'Comparator<Animal>'

Actual behavior:
A successful compilation. Then, at runtime, a type error is thrown at the compare function of CatCompare:

TypeError: x.getClawLength is not a function

Discussion:
Although the compiler of TypeScript is able to catch a similar error as follows:

const cats: Cat[] = [ new Animal(), new Animal() ];
       ^ Type 'Animal[]' is not assignable to type 'Cat[]'.

However, it seems that the compiler only makes sure that the built-in Array type is invariant. For other generic types, variance is not checked.

@RyanCavanaugh Thanks for linking that issue to this one. I would like to point out that this issue is less of a request for variance annotation syntax. The focus here is a type error that is missed by the the compiler, which indicates that the type inference system of TypeScript may not be sound.