I have following code
from typing import TypeVar, Type, overload
T = TypeVar('T')
@overload
def foo(bar: Type[T]) -> T:
...
@overload
def foo(bar: Type[T] | None) -> T | None:
...
def foo(bar: Type[T] | None) -> T | None:
# implementation goes here
...
class Bar:
...
bar = foo(Bar)
bar2 = foo(Bar | None) # No overload variant of "foo" matches argument type "UnionType"
How to properly type hint case for bar2
?
I tried some others:
Type[T | None]
, mypy says Overloaded function signature 2 will never be matched: signature 1's parameter type(s) are the same or broader
removing 2nd overload (resulting in only Type[T]
allowed), mypy says No overload variant of "foo" matches argument type "UnionType"
(meaning 2nd overload is incorrect for that case anyways)
Only concrete classes are assignable to type[T]
:
[...] the actual argument passed in at runtime must [...] be a concrete class object [...]
— Special types in annotations §
type[]
| Python typing spec
(Admittedly, the spec isn't completely clear about this. However, the following paragraph holds true.)
It is thus understood that an object of this type can be invoked to retrieve an instance of type T
. type[T] | None
means such a thing, or None
; thus, bar
might or might not be invocable.
def foo(bar: type[T] | None) -> None:
if bar is not None:
reveal_type(bar) # type[T]
instance = bar() # fine
However, Bar | None
returns an instance of UnionType
at runtime, and objects of this kind cannot be invoked.
foo(Bar | None) # (Bar | None) is not None
# bar() => error
What you want is the proposed TypeForm
of PEP 747 (a draft PEP):
def foo[T](bar: TypeForm[T]) -> T: ...
reveal_type(foo(Bar | None)) # Bar | None