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)
type[T]
implies something that can be invoked to retrieve an instance of type T
. type[T] | None
means such a thing, or None
; thus, bar
might 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
You seem to want the proposed TypeForm
, which, as of yet, only exists in a PEP draft:
@overload
def foo[T](bar: TypeForm[T]) -> T:
...
@overload
def foo[T](bar: TypeForm[T | None]) -> T | None:
...
def foo[T](bar: TypeForm[T | None]) -> T | None:
...
To quote the draft:
Both TypeForm[] and type[] can be used to constrain the same type variable within the same function definition:
def as_instance[T](form: TypeForm[T]) -> T | None: return form() if isinstance(form, type) else None