Skip to content

Variance — when a Dog[] may stand in for an Animal[]

Level: 301 · for TypeScript users who want to know why unsound code compiles

Stub — an outline, not a lesson. There is no runnable example behind this page yet, so nothing on it has been through the check that backs every other claim in this library. The questions below are what the finished page has to answer.

One line: A Dog[] passes as an Animal[] because arrays are compared covariantly; parameters are compared the other way, so onlyDogs(d: Dog) is rejected for an (a: Animal) => void property (TS2322) but accepted for the same member written as a method, where it throws a TypeError.

Keywords: variance, covariance, contravariance, strictFunctionTypes

What the finished page will answer

  • Why does const animals: Animal[] = dogs compile, and what goes wrong once animals is given a Cat?
  • Why is (d: Dog) => void rejected for a property typed (a: Animal) => void but accepted for a method, and what does calling it with an Animal do?
  • Which way do return types vary: is () => Dog assignable to () => Animal, and the reverse?
  • What do the in and out annotations on a type parameter declare, and why does TS2636 catch a wrong out on a property but not on a method?
  • What does --strictFunctionTypes false change in the same file?

Examples it will need

  • variance_method_bivariance_sh.sh — tsc's TS2322 for the property-style handler only, then the TypeError that the accepted method-style handler throws
  • variance_annotations_tserror.ts — TS2636 for an out annotation on a type used as a parameter, and for an in annotation on a return type
  • variance_return_types_ts.ts — a () => Dog used as a () => Animal, and what the caller receives

See also

Sources to start from