-
Based on feedback, it sounds like the above proposal does not sufficiently meet the needs or expectations of users who are dissatisfied with the current Taking in all of the feedback, here's an alternative proposal that involves the introduction of another form of StrictTypeGuard This new flavor of type guard would be similar to the more flexible version defined in PEP 647 with the following three differences:
def is_marsupial(val: Animal) -> StrictTypeGuard[Kangaroo | Koala]: # This is allowed
return isinstance(val, Kangaroo | Koala)
def has_no_nones(val: list[T | None]) -> StrictTypeGuard[list[T]]: # Error: "list[T]" cannot be assigned to "list[T | None]"
return None not in val
def is_black_cat(val: Animal) -> StrictTypeGuard[Cat]:
return isinstance(val, Cat) and val.color == Color.Black
def func(val: Cat | Dog):
if is_black_cat(val):
reveal_type(val) # Cat
else:
reveal_type(val) # Dog - which is potentially wrong here
def is_cardinal_direction(val: str) -> StrictTypeGuard[Literal["N", "S", "E", "W"]]:
return val in ("N", "S", "E", "W")
def func(direction: Literal["NW", "E"]):
if is_cardinal_direction(direction):
reveal_type(direction) # Literal["E"] # The type cannot be "N", "S" or "W" here because of argument type
else:
reveal_type(direction) # Literal["NW"] TypeAssert and StrictTypeAssert function assertAuthenticated(user: User | null): asserts user is User {
if (user === null) {
throw new Error('Unauthenticated');
}
} We propose to add two new forms Here are some examples: def verify_no_nones(val: list[None | T]) -> TypeAssert[list[T]]:
if None in val:
raise ValueError()
def func(x: list[int | None]):
verify_no_nones(x)
reveal_type(x) # list[int] and def assert_is_one_dimensional(val: tuple[T, ...] | T) -> StrictTypeAssert[tuple[T] | T]:
if isinstance(val, tuple) and len(val) != 1:
raise ValueError()
def func(x: float, y: tuple[float, ...]):
assert_is_one_dimensional(x)
reveal_type(x) # float
assert_is_one_dimensional(y)
reveal_type(y) # tuple[float] Thoughts? Suggestions? If we move forward with the above proposal (or some subset thereof), it will probably require a new PEP, as opposed to a modification to PEP 647. |
Beta Was this translation helpful? Give feedback.
-
It's not clear how I can use import typing as t
import typing_extensions as te
from collections import abc
@overload
def is_collection(item: str) -> te.Literal[False]:
...
@overload
def is_collection(item: bytes) -> te.Literal[False]:
...
@overload
def is_collection(item: t.Collection) -> te.Literal[True]:
...
def is_collection(item: t.Any) -> bool:
"""Return True if the item is a collection class but not a string."""
return not isinstance(item, (str, bytes)) and isinstance(item, abc.Collection)
def is_present(collection: t.Set, item: t.Union[str, t.Collection]) -> bool:
if is_collection(item):
# item is still a union here and mypy will flag `str` as unusable for `&`
return bool(collection & item)
return item in collection |
Beta Was this translation helpful? Give feedback.
-
[I posted this in the typing-sig last week but haven't yet received much input. Reposting here for visibility.]
Since the introduction of PEP 647 (User-defined Type Guards), we've received a steady stream of input from users saying that they don't like the limitation that type narrowing is applied only in the positive case and is not applied in the negative case.
In general, type narrowing is not safe to perform if a user-defined type guard returns False, so I've stood by the original decision not to provide type narrowing in the negative case, but there are cases where such narrowing is safe and desirable.
In [a recent thread](#996 (comment)
In this discussion) where this was discussed in some detail, @ikamensh proposed a solution. The proposal is to extend the existing
TypeGuard
to support an optional second type argument. If present, the second argument indicates the narrowed type in the negative type narrowing situation.Here's a simple (admittedly contrived) example:
I've implemented this proposal in pyright so folks can experiment with it and see if they like it. If there's general consensus that it's the right approach, I can file an amendment for the existing PEP 647 to include this functionality. It was trivial to add to pyright, so I'm optimistic that it would likewise be easy to add to the other type checkers.
Another common request for PEP 647 is the desire for an "assert" form of TypeGuard — a way to indicate that a function performs runtime validation of a type, raising an exception if the type is incorrect. It occurred to me that this two-argument form of
TypeGuard
could also be used to handle this use case. The second argument would be specified as aNoReturn
. I've provisionally implemented this in pyright as well.Here's what this would look like:
I'm interested in input on these proposals.
-Eric
--
Eric Traut
Contributor to Pyright & Pylance
Microsoft
Beta Was this translation helpful? Give feedback.