I'm quoting here the content of my two comments on the mypy issue https://github.com/python/mypy/issues/5206:
# Quote starts here Why do we need a boolean return?
A direct solution to this issue would be to implement type-guard functions to return the checked argument, like a checked cast in fact: ```python def as_integer(x) -> int: if not isinstance(x, int): raise ValueError("not an int") return x ``` If-else logic can simply be achieved using try-catch-else blocks. ```python try: checked_x = as_integer(x) except ValueError: ... else: ... # use checked_x as an integer in this block ``` This checked-cast functions can also be used as expression, which is convenient for case like `expect_an_int(as_integer(x))` (but with risks of exception mix up between expect_an_int and as_integer).
Checked-cast functions could also return an `Optional` in order to avoid exception (but that's maybe not suited for some cases) ```python def as_integer(x) -> Optional[int]: return x if isinstance(x, int) else None
if (checked_x := as_integer(x)) is not None : ... else: ... ```
Moreover, if several parameters needs to be type-checked, a tuple returns can do the job: ```python def check_int_and_str(x, y) -> tuple[int, str]: if not isinstance(x, int) or not isinstance(y, str): raise ValueError("bad types") return x, y
checked_x, checked_y = check_int_and_str(x, y) ```
But yes, this solution imply to assign an additional variable (with an additional name), so it's heavier, but **it already works** out of the box and do the job, no PEP required.
That's being said, if boolean type-guard functions have to be implemented in the language (and I would be happy to use them to replace my heavier checked-cast), why not using [PEP 593](https://www.python.org/dev/peps/pep-0593/) `Annotated`? By adding a standard type annotation (for `Annotated`), one could write something like ```python from typing import Annotated, TypeGuard
def is_integer(x) -> Annotated[bool, TypeGuard(int, "x")]: # map the type-guard to the corresponding parameter return isinstance(x, int) ``` That could allow type-guarding of several parameters: ```python def check_int_and_str(x, y) -> Annotated[bool, TypeGuard(int, "x"), TypeGuard(str, "y")]: return isinstance(x, int) and isinstance(y, str) ``` Using a PEP 593 type annotation instead of a whole new type has the advantage of **not impacting every tools using type annotation** (as metadata can just be ignored). This is also exactly the purpose of PEP 593 as it states:
a type T can be annotated with metadata x via the typehint Annotated[T, x]. **This metadata can be used for either static analysis** or at runtime.
The type-guard is indeed a metadata of the function result, but the function still returns a `bool`.
But yes, this solution seems to be a little bit heavier than @vbraun proposal or [PEP 647](https://www.python.org/dev/peps/pep-0647/), but nothing prevent the specification and implementation of my proposal to provide the following shortcuts: - when no parameter name is passed to `TypeGuard`, i.e. `TypeGuard(int)`, it applies to the first parameter (or the second in case of a method) - `TypeGuard` has a `__getitem__` method which gives the following result: `TypeGuard[T] == Annotated[bool, TypeGuard(T)]`
A simple implementation would be: ```python class TypeGuard: def __init__(self, tp, param=None): self.tp = tp if param is not None and not isinstance(param, str): raise TypeError("Type guard parameter mapping must be a string") self.param = param
def __getitem__(self, item): return Annotated[bool, TypeGuard(item)] ```
It would then possible to write ```python def is_integer(x) -> TypeGuard[int]: ... # which would give in fact `def is_integer(x) -> Annotated[bool, TypeGuard(int)]` # which would thus be equivalent to `def is_integer(x) -> Annotated[bool, TypeGuard(int, "x")]` ``` As easy, but more powerful (support arbitrary parameters), and again, less complexity (no additional `SpecialForm`), less impact on existent tools. # Quote ends here
To sum up, checked cast can already "do the job", at the cost of additional variable declaration and exception-catching/optional-checking. Is the PEP worth the cost?
In this case, I think a new "special form" type is not the best way to implement type guard, and I would rather use PEP 593 the way I've described above.