Expected type 'List[A]' (matched generic type 'List[_T]'), got 'List[B]' instead on correctly typed lists

from typing import List


class Base(object):
    pass


class A(Base):
    pass


class B(Base):
    pass


a: List[A] = []
b: List[B] = []
c: List[Base] = a + b

I am getting Expected type 'List[A]' (matched generic type 'List[_T]'), got 'List[B]' instead on b.

How do I get correct warnings, as obviously the types are fine.


These types are not fine. List is invariant, meaning that a List[X] is not a substitute for List[Y] unless X and Y are exactly equal. Similarly, A <: Base does not imply List[A] <: List[Base] and likewise for B.

PEP 484: Covariance and contravriance

[...]
By default generic types are considered invariant in all type variables, which means that values for variables annotated with types like List[Employee] must exactly match the type annotation -- no subclasses or superclasses of the type parameter (in this example Employee) are allowed.

While it is possible to re-interpret the types for this operation, this is not unambiguous. A conservative type checker will reject the operation instead of guessing.


A mutable container such as List is invariant because elements can be both inserted into (contravariant) and taken from (covariant) the list. If mutability is not required, using an immutable Sequence provides valid type annotation:

from typing import Sequence

a: Sequence[A] = []
b: Sequence[B] = []
c: Sequence[Base] = [*a, *b]

If mutability is desired, one can explicitly enumerate all the types to be found in the List. This pre-emptively widens the elements to be expected in the list, even if each individual list actually contains only one type.

a: List[Union[A, B]] = []
b: List[Union[A, B]] = []
c: List[Union[A, B]] = a + b

Pre-emptively widening the type of the operands may be undesirable or impossible. Alternatively, one can cast them at the site of usage.

a: List[A] = []
b: List[B] = []
c: List[Union[A, B]] = cast(List[Union[A, B]], a) + cast(List[Union[A, B]], a)

Note that cast effectively disables type checking for the cast'ed value. Only use it in cases that are known to be correct.