On Sat, Oct 3, 2020 at 12:56 PM Jake Bailey via Typing-sig <typing-sig@python.org> wrote:
I've been thinking about this one, and I think that it would be better to not make `TypeGuard` an instantiable type, instead having it be a special form that type checkers treat as `bool`. There's already some precedence for types that "don't really exist" here with `Literal`, where you wouldn't write `return Literal(2)`, but just `return 2`.

It's true for all special forms, right? You don't use Union when a return type is `Union[A, B]`, and you don't use Callable when returning a function.
Firstly, I think this will make the functions read better. E.g.:

def is_two_element_tuple(val: Tuple[_T, ...]) -> TypeGuard[Tuple[_T, _T]]:
    return len(val) == 2

def is_string_list(val: List[object]) -> TypeGuard[List[str]]:
    return all(isinstance(x, str) for x in val)

Secondly (like `Literal`), not introducing a type here would mean that existing functions could be converted to type guards without modification. For example, there are a few functions in `inspect` that could be converted to type guards:

# Before
def ismodule(object: object) -> bool: ...
# After, maybe
from types import ModuleType
def ismodule(object: object) -> TypeGuard[ModuleType]: ...

If we required type guard functions to return an instance of `TypeGuard`, then no existing function could become a guard, because they will return `TypeGuard`'s parent type.

I don't understand the latter phrase (what's TypeGuard's parent type anyway?) but indeed it seems like a friendlier API.

That said, I think I've wanted this kind of functionality a few times, and long ago I recall the mypy team discussing this. For example in https://github.com/python/mypy/issues/5206, which mentions TypeScript type guards.

I also feel however that the need for this is much less in Python than in TypeScript -- in most cases (though not the examples Eric showed) you can just use isinstance(), since Python doesn't have the feature that makes this needed in TypeScript (Python's class instances aren't dicts). So I wouldn't hurry to implement this.

Possibly pyright could prototype this as a pyright extension and you could propose a PEP based on your experience using that.

--Guido van Rossum (python.org/~guido)