-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Type-narrowing based on x in y
#9338
Comments
x in y
x in y
May be related, but I'm trying to do something like this: import typing
class MyType(typing.TypedDict):
a: int
b: typing.NotRequired[int]
MyEnum = typing.Literal["a", "b"]
def foo(t1: MyType, t2: MyType) -> None:
for key in t1.keys():
if key not in t2:
continue
key = typing.cast(MyEnum, key)
print(t2[key]) is it possible to do something like this currently, where pyright doesn't raise an issue? |
I want to contribute to this even though I am here for the first time. I hope that is fine. |
This is probably a design change, which may or may not be wanted depending on the spec, what other checkers do etc., so I'd suggest wait for confirmation from maintainers. MyPy also doesn't do this right now for example, but it has been on the roadmap for a long time: |
@tusharsadhwani, the behavior you're seeing is not related to this issue. The OP has identified a bug in the |
I've just run into this myself today, I think. Code sample in pyright playground from typing import assert_type
def g(a: int | None):
if a is not None:
assert_type(a, int) #works fine
def f(a: int | None):
if a not in [None]:
assert_type(a, int) # "assert_type" mismatch: expected "int" but received "int | None" (reportAssertTypeFailure) |
@wyattscarpenter, that's not related to this issue. Type narrowing in the negative case in your code sample will not eliminate |
Ah, thanks! |
I believe I recently ran into this. From the playground. from typing import Literal
def f(a: Literal['AND', '&', 'OR', '|']):
if a in {'AND', '&'}:
reveal_type(a) # Type of "a" is "Literal['AND', '&', 'OR', '|']" # Seems wrong
if a in {'OR', '|'}:
reveal_type(a) # Type of "a" is "Literal['AND', '&', 'OR', '|']" # Seem wrong However, if I change from set to tuple it works. from typing import Literal
def f(a: Literal['AND', '&', 'OR', '|']):
if a in ('AND', '&'):
reveal_type(a) # Type of "a" is "Literal['AND', '&']"
if a in ('OR', '|'):
reveal_type(a) # Type of "a" is "Literal['OR', '|']" |
@Sxderp, the behavior you're seeing here is expected. Pyright's inference behaviors for set, list and dict expressions do not retain literals because these types are mutable. Tuples are immutable, so pyright retains literals (in most cases). For details, refer to the pyright documentation here and here. |
Maybe the documentation for type-guards should be clarified or a link should be added to the type-inference page? https://github.com/microsoft/pyright/blob/main/docs/type-concepts-advanced.md
This is the line for my source of confusion. |
Converted from discussion (#9337).
We can now have following snippet pass type-checking -
The type-narrowing of
x
can be a false negative, as the default value0.0
ofx
is obviously not an instance ofint
.On the other hand, type-narrowing based on
x == L
seems to work soundly -My suggestion is that type-narrowing based of
x in y
only take effect when the element type ofy
is a literal type (or maybe a union of literal types that shares a same runtime type) and type ofx
is the runtime type ofy
(or some related union types).My expected behavior would be like following examples -
The text was updated successfully, but these errors were encountered: