I have a function which takes a list of a base class as argument, and I have a variable which is a list of a derived class. Using this variable as the argument gives mypy error: Argument 1 to "do_stuff" has incompatible type "List[DerivedClass]"; expected "List[BaseClass]".
class BaseClass(TypedDict):
base_field: str
class DerivedClass(BaseClass):
derived_field: str
def do_stuff(data: List[BaseClass]) -> None:
pass
foo: List[DerivedClass] = [{'base_field': 'foo', 'derived_field': 'bar'}]
do_stuff(foo)
If the argument and variable are instead BaseClass and DerivedClass respectively, i.e. not lists, it understands that the variable can be casted implicitly to the base class. But for lists it doesn’t work. How can I solve this, preferably other than #type: ignore.
>Solution :
It depends on what exactly do_stuff is doing, but nine times out of ten the best solution is to use Sequence instead of List:
from typing import Sequence
def do_stuff(data: Sequence[BaseClass]) -> None:
pass
The reason you can’t use List[BaseClass] here is that do_stuff would be allowed to add BaseClass instances to data, which would in turn break foo in the caller. Sequence doesn’t imply mutability, so do_stuff is not allowed (static-typing-wise) to modify a Sequence parameter, which prevents that issue. (Put differently, Sequence is covariant and List is invariant. Most mutable generics are invariant because of exactly this issue.)
If do_stuff does need to mutate data, you’ll need to rethink the typing — should it be allowed to add a BaseClass to it? If not, maybe do_stuff should take a List[DerivedClass]. If so, you need to declare foo as a List[BaseClass] to account for that possibility.