Description
In Dart, a parameterized class type is covariant in every type parameter; for example, List<int>
is a subtype of List<num>
because int
is a subtype of num
(so the list type and its type argument "co-vary").
This is sound for all covariant occurrences of such type parameters in the class body (for instance, the getter first
of a list has return type E
, which is sound). It is also sound for contravariant occurrences when a sufficiently exact receiver type is known (e.g., for a literal like <num>[].add(4.2)
, or for a generative constructor SomeClass<num>.foo(4.2)
).
However, in general, every member access where a covariant type parameter occurs in a contravariant position may cause a dynamic type error, because the actual type annotation at run time—say, the type of a parameter of a method—is a subtype of the one which is known at compile-time.
For example: Assume that a variable xs
has declared type List<num>
, and consider the invocation xs.add(4.2)
. This invocation will fail if the value of xs
is a List<int>
, but it will succeed with a List<num>
and with a List<double>
. Still, the static type of xs
allows them all.
This issue is a request for improvements in the amount of control that developers have over this kind of dynamic checks. In particular, it should be possible to establish a guarantee at compile time that such a dynamic type error cannot occur.