Search code examples
pythonmypypython-typing

Incompatible types in assignment (expression has type "List[str]", variable has type "List[Union[str, int]]")


If I run this code through mypy:

if __name__ == '__main__':
    list1: list[str] = []
    list2: list[str | int] = []
    list2 = list1

It gives me following error:

error: Incompatible types in assignment (expression has type "List[str]", variable has type "List[Union[str, int]]")

Why? Isn't a list[str] a subset of list[str | int]? If yes, then why can't I assign list1 which doesn't have wider range of possible types to list2?


Solution

  • Generic types can be classified as covariant, contravariant, or invariant. When u is a subtype of v, a generic type g is

    1. covariant when g[u] is a subtype of g[v],
    2. contravariant when g[v] is a subtype of g[u], or
    3. invariant when neither g[u] nor g[v] is a subtype of the other.

    list is invariant because lists are mutable, in which case it is not safe to substitute one concrete type of list for another. Immutable sequences are covariant: Sequence[str] is, indeed, a subtype of Sequence[str | int], so you could do the following:

    from collections.abc import Sequence
    
    
    if __name__ == '__main__':
        list1: Sequence[str] = []
        list2: Sequence[str | int] = []
        list2 = list1
    

    The only thing you can do with a Sequence, regardless of whether it is a list, tuple, etc, is read from it, and anything reading from list2 will know it can't assume that any given element is a str or an int (though it will be one or the other, rather than, say, a float), so it's safe to provide a sequence of either type.