Getting this typing error:
error: Incompatible types in assignment (expression has type "object", variable has type "A | B") [assignment]
With this code:
from dataclasses import dataclass
from typing import TypeVar, Mapping, reveal_type
@dataclass
class A:
foo: str = "a"
@dataclass
class B:
bar: str = "b"
lookup_table: Mapping[str, type[A] | type[B]] = {
"a": A,
"b": B
}
reveal_type(lookup_table) # note: Revealed type is "typing.Mapping[builtins.str, Union[type[simple.A], type[simple.B]]]"
T = TypeVar("T")
def load(lookup_table: Mapping[str, type[T]], lookup_key:str) -> T:
con: type[T] = lookup_table[lookup_key]
instance: T = con()
return instance
example_a: A | B = load(lookup_table, "a") # error: Incompatible types in assignment (expression has type "object", variable has type "A | B")
print(example_a)
Edit: Logged a mypy bug here: https://github.com/python/mypy/issues/18265
This is a mypy
bug present in 1.13.0 and below (previously reported here and by OP here). pyright
and basedmypy
both accept the given snippet.
mypy
stores type[A | B]
types as a union of types internally (type[A] | type[B]
). This is usually convenient, but causes troubles when solving type[T] <: type[A] | type[B]
for T
, because most types aren't "distributive" (P[A, B]
is not equivalent to P[A] | P[B]
), and type
special case isn't taken into account yet.
General solver produces meet(A, B)
in such case by solving
type[T] <: type[A] => T <: A |
| => T <: meet(A, B)
type[T] <: type[B] => T <: B |
When A
and B
have no explicit parent in common, the closest supertype is object
. To understand the logic behind that, consider similar equations: x < 2 && x < 3 => x < min(2, 3)
, and intersection/meet is for types what min operation is for numbers (and union/join is similar to max).
I submitted a PR to special-case this behaviour, so things may change in a future mypy
release.