[TypeGuard] is not a subtype of bool. Therefore, Callable[..., TypeGuard[int]] is not assignable to Callable[..., bool] .
That means that the following program should be rejected by type checkers:
from itertools import count
from typing import TypeGuard, Callable, Iterable
def filtered_count(pred: Callable[[int], bool]) -> Iterable[int]:
for i in count():
if pred(i):
yield i
def is_even_int(x: object) -> TypeGuard[int]:
return isinstance(x, int) and x % 2 == 0
for i in filtered_count(is_even_int):
print(i)
This is passing a function returning a TypeGuard to a parameter of type Callable[[int], bool], and the spec explicitly says that is not allowed.
However, both mypy and pyright accept this program without errors. It works fine at runtime, and I donāt see any soundness problems that result from accepting it.
The rule in the spec derives from PEP 647 (which created TypeGuard) and was added in this PR. @erictrauttold me that the change derived from feedback by Guido, but I havenāt been able to find a more precise motivation.
I propose to change the spec so that Callable[..., TypeGuard[...]] is a subtype of Callable[..., bool]. This brings the spec in line with the actual behavior of major type checkers.
The same reasoning applies to the new TypeIs special form I propose in PEP 742. For the moment I copied the TypeGuard restriction in the new PEP, but if the proposal in this post is accepted, Iāll change PEP 742 accordingly.
Iām also in favor. Regarding my original objection, itās possible that I objected to TypeGuard[...] being considered a subtype of bool everywhere, rather than in the specific case where itās the return type of a Callable type. FWIW, in my recollection I wasnāt the one who originally came up with this argument, but (a) memory is fungible, and (b) it doesnāt matter assuming we all agree on this now.