diff --git a/docs/type-concepts.md b/docs/type-concepts.md index 3c40f2892..3b9b4a03a 100644 --- a/docs/type-concepts.md +++ b/docs/type-concepts.md @@ -156,7 +156,7 @@ In addition to assignment-based type narrowing, Pyright supports the following t * `x[K] == V` and `x[K] != V` (where K and V are literal expressions and x is a type that is distinguished by a TypedDict field with a literal type) * `x[I] == V` and `x[I] != V` (where I and V are literal expressions and x is a known-length tuple that is distinguished by the index indicated by I) * `x[I] is None` and `x[I] is not None` (where I is a literal expression and x is a known-length tuple that is distinguished by the index indicated by I) -* `x in y` (where y is instance of list, set, frozenset, or deque) +* `x in y` (where y is instance of list, set, frozenset, deque, or tuple) * `S in D` and `S not in D` (where S is a string literal and D is a TypedDict) * `isinstance(x, T)` (where T is a type or a tuple of types) * `issubclass(x, T)` (where T is a type or a tuple of types) diff --git a/packages/pyright-internal/src/analyzer/typeGuards.ts b/packages/pyright-internal/src/analyzer/typeGuards.ts index ee2b78a16..e8ffe5a99 100644 --- a/packages/pyright-internal/src/analyzer/typeGuards.ts +++ b/packages/pyright-internal/src/analyzer/typeGuards.ts @@ -954,7 +954,7 @@ function narrowTypeForContains(evaluator: TypeEvaluator, referenceType: Type, co const builtInName = containerType.details.name; - if (!['list', 'set', 'frozenset', 'deque'].some((name) => name === builtInName)) { + if (!['list', 'set', 'frozenset', 'deque', 'tuple'].some((name) => name === builtInName)) { return referenceType; } diff --git a/packages/pyright-internal/src/tests/samples/typeNarrowingIn1.py b/packages/pyright-internal/src/tests/samples/typeNarrowingIn1.py index 9495211b5..29f9c45c9 100644 --- a/packages/pyright-internal/src/tests/samples/typeNarrowingIn1.py +++ b/packages/pyright-internal/src/tests/samples/typeNarrowingIn1.py @@ -1,6 +1,6 @@ # This sample tests type narrowing for the "in" operator. -from typing import Optional, Union +from typing import Literal, Optional, Union import random @@ -38,3 +38,14 @@ if y in [2]: # This should generate an error because y should # be narrowed to an int. verify_str(y) + + +def func1(x: Optional[Union[int, str]]): + if x in (1, 2): + t1: Literal["int"] = reveal_type(x) + + if x in (1, "2"): + t2: Literal["int | str"] = reveal_type(x) + + if x in (1, None): + t3: Literal["int | None"] = reveal_type(x)