blob: 291f73a45230560d0596ce2cee8c53b5acb8567e [file] [log] [blame] [edit]
[case testNarrowingParentWithStrsBasic]
# flags: --python-version 3.7
from dataclasses import dataclass
from typing import NamedTuple, Tuple, Union
from typing_extensions import Literal, TypedDict
class Object1:
key: Literal["A"]
foo: int
class Object2:
key: Literal["B"]
bar: str
@dataclass
class Dataclass1:
key: Literal["A"]
foo: int
@dataclass
class Dataclass2:
key: Literal["B"]
foo: str
class NamedTuple1(NamedTuple):
key: Literal["A"]
foo: int
class NamedTuple2(NamedTuple):
key: Literal["B"]
foo: str
Tuple1 = Tuple[Literal["A"], int]
Tuple2 = Tuple[Literal["B"], str]
class TypedDict1(TypedDict):
key: Literal["A"]
foo: int
class TypedDict2(TypedDict):
key: Literal["B"]
foo: str
x1: Union[Object1, Object2]
if x1.key == "A":
reveal_type(x1) # N: Revealed type is "__main__.Object1"
reveal_type(x1.key) # N: Revealed type is "Literal['A']"
else:
reveal_type(x1) # N: Revealed type is "__main__.Object2"
reveal_type(x1.key) # N: Revealed type is "Literal['B']"
x2: Union[Dataclass1, Dataclass2]
if x2.key == "A":
reveal_type(x2) # N: Revealed type is "__main__.Dataclass1"
reveal_type(x2.key) # N: Revealed type is "Literal['A']"
else:
reveal_type(x2) # N: Revealed type is "__main__.Dataclass2"
reveal_type(x2.key) # N: Revealed type is "Literal['B']"
x3: Union[NamedTuple1, NamedTuple2]
if x3.key == "A":
reveal_type(x3) # N: Revealed type is "Tuple[Literal['A'], builtins.int, fallback=__main__.NamedTuple1]"
reveal_type(x3.key) # N: Revealed type is "Literal['A']"
else:
reveal_type(x3) # N: Revealed type is "Tuple[Literal['B'], builtins.str, fallback=__main__.NamedTuple2]"
reveal_type(x3.key) # N: Revealed type is "Literal['B']"
if x3[0] == "A":
reveal_type(x3) # N: Revealed type is "Tuple[Literal['A'], builtins.int, fallback=__main__.NamedTuple1]"
reveal_type(x3[0]) # N: Revealed type is "Literal['A']"
else:
reveal_type(x3) # N: Revealed type is "Tuple[Literal['B'], builtins.str, fallback=__main__.NamedTuple2]"
reveal_type(x3[0]) # N: Revealed type is "Literal['B']"
x4: Union[Tuple1, Tuple2]
if x4[0] == "A":
reveal_type(x4) # N: Revealed type is "Tuple[Literal['A'], builtins.int]"
reveal_type(x4[0]) # N: Revealed type is "Literal['A']"
else:
reveal_type(x4) # N: Revealed type is "Tuple[Literal['B'], builtins.str]"
reveal_type(x4[0]) # N: Revealed type is "Literal['B']"
x5: Union[TypedDict1, TypedDict2]
if x5["key"] == "A":
reveal_type(x5) # N: Revealed type is "TypedDict('__main__.TypedDict1', {'key': Literal['A'], 'foo': builtins.int})"
else:
reveal_type(x5) # N: Revealed type is "TypedDict('__main__.TypedDict2', {'key': Literal['B'], 'foo': builtins.str})"
[builtins fixtures/primitives.pyi]
[case testNarrowingParentWithEnumsBasic]
# flags: --python-version 3.7
from enum import Enum
from dataclasses import dataclass
from typing import NamedTuple, Tuple, Union
from typing_extensions import Literal, TypedDict
class Key(Enum):
A = 1
B = 2
C = 3
class Object1:
key: Literal[Key.A]
foo: int
class Object2:
key: Literal[Key.B]
bar: str
@dataclass
class Dataclass1:
key: Literal[Key.A]
foo: int
@dataclass
class Dataclass2:
key: Literal[Key.B]
foo: str
class NamedTuple1(NamedTuple):
key: Literal[Key.A]
foo: int
class NamedTuple2(NamedTuple):
key: Literal[Key.B]
foo: str
Tuple1 = Tuple[Literal[Key.A], int]
Tuple2 = Tuple[Literal[Key.B], str]
class TypedDict1(TypedDict):
key: Literal[Key.A]
foo: int
class TypedDict2(TypedDict):
key: Literal[Key.B]
foo: str
x1: Union[Object1, Object2]
if x1.key is Key.A:
reveal_type(x1) # N: Revealed type is "__main__.Object1"
reveal_type(x1.key) # N: Revealed type is "Literal[__main__.Key.A]"
else:
reveal_type(x1) # N: Revealed type is "__main__.Object2"
reveal_type(x1.key) # N: Revealed type is "Literal[__main__.Key.B]"
x2: Union[Dataclass1, Dataclass2]
if x2.key is Key.A:
reveal_type(x2) # N: Revealed type is "__main__.Dataclass1"
reveal_type(x2.key) # N: Revealed type is "Literal[__main__.Key.A]"
else:
reveal_type(x2) # N: Revealed type is "__main__.Dataclass2"
reveal_type(x2.key) # N: Revealed type is "Literal[__main__.Key.B]"
x3: Union[NamedTuple1, NamedTuple2]
if x3.key is Key.A:
reveal_type(x3) # N: Revealed type is "Tuple[Literal[__main__.Key.A], builtins.int, fallback=__main__.NamedTuple1]"
reveal_type(x3.key) # N: Revealed type is "Literal[__main__.Key.A]"
else:
reveal_type(x3) # N: Revealed type is "Tuple[Literal[__main__.Key.B], builtins.str, fallback=__main__.NamedTuple2]"
reveal_type(x3.key) # N: Revealed type is "Literal[__main__.Key.B]"
if x3[0] is Key.A:
reveal_type(x3) # N: Revealed type is "Tuple[Literal[__main__.Key.A], builtins.int, fallback=__main__.NamedTuple1]"
reveal_type(x3[0]) # N: Revealed type is "Literal[__main__.Key.A]"
else:
reveal_type(x3) # N: Revealed type is "Tuple[Literal[__main__.Key.B], builtins.str, fallback=__main__.NamedTuple2]"
reveal_type(x3[0]) # N: Revealed type is "Literal[__main__.Key.B]"
x4: Union[Tuple1, Tuple2]
if x4[0] is Key.A:
reveal_type(x4) # N: Revealed type is "Tuple[Literal[__main__.Key.A], builtins.int]"
reveal_type(x4[0]) # N: Revealed type is "Literal[__main__.Key.A]"
else:
reveal_type(x4) # N: Revealed type is "Tuple[Literal[__main__.Key.B], builtins.str]"
reveal_type(x4[0]) # N: Revealed type is "Literal[__main__.Key.B]"
x5: Union[TypedDict1, TypedDict2]
if x5["key"] is Key.A:
reveal_type(x5) # N: Revealed type is "TypedDict('__main__.TypedDict1', {'key': Literal[__main__.Key.A], 'foo': builtins.int})"
else:
reveal_type(x5) # N: Revealed type is "TypedDict('__main__.TypedDict2', {'key': Literal[__main__.Key.B], 'foo': builtins.str})"
[builtins fixtures/narrowing.pyi]
[case testNarrowingParentWithIsInstanceBasic]
# flags: --python-version 3.7
from dataclasses import dataclass
from typing import NamedTuple, Tuple, Union
from typing_extensions import TypedDict
class Object1:
key: int
class Object2:
key: str
@dataclass
class Dataclass1:
key: int
@dataclass
class Dataclass2:
key: str
class NamedTuple1(NamedTuple):
key: int
class NamedTuple2(NamedTuple):
key: str
Tuple1 = Tuple[int]
Tuple2 = Tuple[str]
class TypedDict1(TypedDict):
key: int
class TypedDict2(TypedDict):
key: str
x1: Union[Object1, Object2]
if isinstance(x1.key, int):
reveal_type(x1) # N: Revealed type is "__main__.Object1"
else:
reveal_type(x1) # N: Revealed type is "__main__.Object2"
x2: Union[Dataclass1, Dataclass2]
if isinstance(x2.key, int):
reveal_type(x2) # N: Revealed type is "__main__.Dataclass1"
else:
reveal_type(x2) # N: Revealed type is "__main__.Dataclass2"
x3: Union[NamedTuple1, NamedTuple2]
if isinstance(x3.key, int):
reveal_type(x3) # N: Revealed type is "Tuple[builtins.int, fallback=__main__.NamedTuple1]"
else:
reveal_type(x3) # N: Revealed type is "Tuple[builtins.str, fallback=__main__.NamedTuple2]"
if isinstance(x3[0], int):
reveal_type(x3) # N: Revealed type is "Tuple[builtins.int, fallback=__main__.NamedTuple1]"
else:
reveal_type(x3) # N: Revealed type is "Tuple[builtins.str, fallback=__main__.NamedTuple2]"
x4: Union[Tuple1, Tuple2]
if isinstance(x4[0], int):
reveal_type(x4) # N: Revealed type is "Tuple[builtins.int]"
else:
reveal_type(x4) # N: Revealed type is "Tuple[builtins.str]"
x5: Union[TypedDict1, TypedDict2]
if isinstance(x5["key"], int):
reveal_type(x5) # N: Revealed type is "TypedDict('__main__.TypedDict1', {'key': builtins.int})"
else:
reveal_type(x5) # N: Revealed type is "TypedDict('__main__.TypedDict2', {'key': builtins.str})"
[builtins fixtures/narrowing.pyi]
[case testNarrowingParentMultipleKeys]
# flags: --warn-unreachable
from enum import Enum
from typing import Union
from typing_extensions import Literal
class Key(Enum):
A = 1
B = 2
C = 3
D = 4
class Object1:
key: Literal[Key.A, Key.C]
class Object2:
key: Literal[Key.B, Key.C]
x: Union[Object1, Object2]
if x.key is Key.A:
reveal_type(x) # N: Revealed type is "__main__.Object1"
else:
reveal_type(x) # N: Revealed type is "Union[__main__.Object1, __main__.Object2]"
if x.key is Key.C:
reveal_type(x) # N: Revealed type is "Union[__main__.Object1, __main__.Object2]"
else:
reveal_type(x) # N: Revealed type is "Union[__main__.Object1, __main__.Object2]"
if x.key is Key.D:
reveal_type(x) # E: Statement is unreachable
else:
reveal_type(x) # N: Revealed type is "Union[__main__.Object1, __main__.Object2]"
[builtins fixtures/tuple.pyi]
[case testNarrowingTypedDictParentMultipleKeys]
# flags: --warn-unreachable
from typing import Union
from typing_extensions import Literal, TypedDict
class TypedDict1(TypedDict):
key: Literal['A', 'C']
class TypedDict2(TypedDict):
key: Literal['B', 'C']
x: Union[TypedDict1, TypedDict2]
if x['key'] == 'A':
reveal_type(x) # N: Revealed type is "TypedDict('__main__.TypedDict1', {'key': Union[Literal['A'], Literal['C']]})"
else:
reveal_type(x) # N: Revealed type is "Union[TypedDict('__main__.TypedDict1', {'key': Union[Literal['A'], Literal['C']]}), TypedDict('__main__.TypedDict2', {'key': Union[Literal['B'], Literal['C']]})]"
if x['key'] == 'C':
reveal_type(x) # N: Revealed type is "Union[TypedDict('__main__.TypedDict1', {'key': Union[Literal['A'], Literal['C']]}), TypedDict('__main__.TypedDict2', {'key': Union[Literal['B'], Literal['C']]})]"
else:
reveal_type(x) # N: Revealed type is "Union[TypedDict('__main__.TypedDict1', {'key': Union[Literal['A'], Literal['C']]}), TypedDict('__main__.TypedDict2', {'key': Union[Literal['B'], Literal['C']]})]"
if x['key'] == 'D':
reveal_type(x) # E: Statement is unreachable
else:
reveal_type(x) # N: Revealed type is "Union[TypedDict('__main__.TypedDict1', {'key': Union[Literal['A'], Literal['C']]}), TypedDict('__main__.TypedDict2', {'key': Union[Literal['B'], Literal['C']]})]"
[builtins fixtures/primitives.pyi]
[case testNarrowingPartialTypedDictParentMultipleKeys]
# flags: --warn-unreachable
from typing import Union
from typing_extensions import Literal, TypedDict
class TypedDict1(TypedDict, total=False):
key: Literal['A', 'C']
class TypedDict2(TypedDict, total=False):
key: Literal['B', 'C']
x: Union[TypedDict1, TypedDict2]
if x['key'] == 'A':
reveal_type(x) # N: Revealed type is "TypedDict('__main__.TypedDict1', {'key'?: Union[Literal['A'], Literal['C']]})"
else:
reveal_type(x) # N: Revealed type is "Union[TypedDict('__main__.TypedDict1', {'key'?: Union[Literal['A'], Literal['C']]}), TypedDict('__main__.TypedDict2', {'key'?: Union[Literal['B'], Literal['C']]})]"
if x['key'] == 'C':
reveal_type(x) # N: Revealed type is "Union[TypedDict('__main__.TypedDict1', {'key'?: Union[Literal['A'], Literal['C']]}), TypedDict('__main__.TypedDict2', {'key'?: Union[Literal['B'], Literal['C']]})]"
else:
reveal_type(x) # N: Revealed type is "Union[TypedDict('__main__.TypedDict1', {'key'?: Union[Literal['A'], Literal['C']]}), TypedDict('__main__.TypedDict2', {'key'?: Union[Literal['B'], Literal['C']]})]"
if x['key'] == 'D':
reveal_type(x) # E: Statement is unreachable
else:
reveal_type(x) # N: Revealed type is "Union[TypedDict('__main__.TypedDict1', {'key'?: Union[Literal['A'], Literal['C']]}), TypedDict('__main__.TypedDict2', {'key'?: Union[Literal['B'], Literal['C']]})]"
[builtins fixtures/primitives.pyi]
[case testNarrowingNestedTypedDicts]
from typing import Union
from typing_extensions import TypedDict, Literal
class A(TypedDict):
key: Literal['A']
class B(TypedDict):
key: Literal['B']
class C(TypedDict):
key: Literal['C']
class X(TypedDict):
inner: Union[A, B]
class Y(TypedDict):
inner: Union[B, C]
unknown: Union[X, Y]
if unknown['inner']['key'] == 'A':
reveal_type(unknown) # N: Revealed type is "TypedDict('__main__.X', {'inner': Union[TypedDict('__main__.A', {'key': Literal['A']}), TypedDict('__main__.B', {'key': Literal['B']})]})"
reveal_type(unknown['inner']) # N: Revealed type is "TypedDict('__main__.A', {'key': Literal['A']})"
if unknown['inner']['key'] == 'B':
reveal_type(unknown) # N: Revealed type is "Union[TypedDict('__main__.X', {'inner': Union[TypedDict('__main__.A', {'key': Literal['A']}), TypedDict('__main__.B', {'key': Literal['B']})]}), TypedDict('__main__.Y', {'inner': Union[TypedDict('__main__.B', {'key': Literal['B']}), TypedDict('__main__.C', {'key': Literal['C']})]})]"
reveal_type(unknown['inner']) # N: Revealed type is "TypedDict('__main__.B', {'key': Literal['B']})"
if unknown['inner']['key'] == 'C':
reveal_type(unknown) # N: Revealed type is "TypedDict('__main__.Y', {'inner': Union[TypedDict('__main__.B', {'key': Literal['B']}), TypedDict('__main__.C', {'key': Literal['C']})]})"
reveal_type(unknown['inner']) # N: Revealed type is "TypedDict('__main__.C', {'key': Literal['C']})"
[builtins fixtures/primitives.pyi]
[case testNarrowingParentWithMultipleParents]
from enum import Enum
from typing import Union
from typing_extensions import Literal
class Key(Enum):
A = 1
B = 2
C = 3
class Object1:
key: Literal[Key.A]
class Object2:
key: Literal[Key.B]
class Object3:
key: Literal[Key.C]
class Object4:
key: str
x: Union[Object1, Object2, Object3, Object4]
if x.key is Key.A:
reveal_type(x) # N: Revealed type is "__main__.Object1"
else:
reveal_type(x) # N: Revealed type is "Union[__main__.Object2, __main__.Object3, __main__.Object4]"
if isinstance(x.key, str):
reveal_type(x) # N: Revealed type is "__main__.Object4"
else:
reveal_type(x) # N: Revealed type is "Union[__main__.Object1, __main__.Object2, __main__.Object3]"
[builtins fixtures/isinstance.pyi]
[case testNarrowingParentsWithGenerics]
from typing import Union, TypeVar, Generic
T = TypeVar('T')
class Wrapper(Generic[T]):
key: T
x: Union[Wrapper[int], Wrapper[str]]
if isinstance(x.key, int):
reveal_type(x) # N: Revealed type is "__main__.Wrapper[builtins.int]"
else:
reveal_type(x) # N: Revealed type is "__main__.Wrapper[builtins.str]"
[builtins fixtures/isinstance.pyi]
[case testNarrowingParentWithParentMixtures]
from enum import Enum
from typing import Union, NamedTuple
from typing_extensions import Literal, TypedDict
class Key(Enum):
A = 1
B = 2
C = 3
class KeyedObject:
key: Literal[Key.A]
class KeyedTypedDict(TypedDict):
key: Literal[Key.B]
class KeyedNamedTuple(NamedTuple):
key: Literal[Key.C]
ok_mixture: Union[KeyedObject, KeyedNamedTuple]
if ok_mixture.key is Key.A:
reveal_type(ok_mixture) # N: Revealed type is "__main__.KeyedObject"
else:
reveal_type(ok_mixture) # N: Revealed type is "Tuple[Literal[__main__.Key.C], fallback=__main__.KeyedNamedTuple]"
impossible_mixture: Union[KeyedObject, KeyedTypedDict]
if impossible_mixture.key is Key.A: # E: Item "KeyedTypedDict" of "Union[KeyedObject, KeyedTypedDict]" has no attribute "key"
reveal_type(impossible_mixture) # N: Revealed type is "Union[__main__.KeyedObject, TypedDict('__main__.KeyedTypedDict', {'key': Literal[__main__.Key.B]})]"
else:
reveal_type(impossible_mixture) # N: Revealed type is "Union[__main__.KeyedObject, TypedDict('__main__.KeyedTypedDict', {'key': Literal[__main__.Key.B]})]"
if impossible_mixture["key"] is Key.A: # E: Value of type "Union[KeyedObject, KeyedTypedDict]" is not indexable
reveal_type(impossible_mixture) # N: Revealed type is "Union[__main__.KeyedObject, TypedDict('__main__.KeyedTypedDict', {'key': Literal[__main__.Key.B]})]"
else:
reveal_type(impossible_mixture) # N: Revealed type is "Union[__main__.KeyedObject, TypedDict('__main__.KeyedTypedDict', {'key': Literal[__main__.Key.B]})]"
weird_mixture: Union[KeyedTypedDict, KeyedNamedTuple]
if weird_mixture["key"] is Key.B: # E: No overload variant of "__getitem__" of "tuple" matches argument type "str" \
# N: Possible overload variants: \
# N: def __getitem__(self, int, /) -> Literal[Key.C] \
# N: def __getitem__(self, slice, /) -> Tuple[Literal[Key.C], ...]
reveal_type(weird_mixture) # N: Revealed type is "Union[TypedDict('__main__.KeyedTypedDict', {'key': Literal[__main__.Key.B]}), Tuple[Literal[__main__.Key.C], fallback=__main__.KeyedNamedTuple]]"
else:
reveal_type(weird_mixture) # N: Revealed type is "Union[TypedDict('__main__.KeyedTypedDict', {'key': Literal[__main__.Key.B]}), Tuple[Literal[__main__.Key.C], fallback=__main__.KeyedNamedTuple]]"
if weird_mixture[0] is Key.B: # E: TypedDict key must be a string literal; expected one of ("key")
reveal_type(weird_mixture) # N: Revealed type is "Union[TypedDict('__main__.KeyedTypedDict', {'key': Literal[__main__.Key.B]}), Tuple[Literal[__main__.Key.C], fallback=__main__.KeyedNamedTuple]]"
else:
reveal_type(weird_mixture) # N: Revealed type is "Union[TypedDict('__main__.KeyedTypedDict', {'key': Literal[__main__.Key.B]}), Tuple[Literal[__main__.Key.C], fallback=__main__.KeyedNamedTuple]]"
[builtins fixtures/tuple.pyi]
[typing fixtures/typing-full.pyi]
[case testNarrowingParentWithProperties]
from enum import Enum
from typing import Union
from typing_extensions import Literal
class Key(Enum):
A = 1
B = 2
C = 3
class Object1:
key: Literal[Key.A]
class Object2:
@property
def key(self) -> Literal[Key.A]: ...
class Object3:
@property
def key(self) -> Literal[Key.B]: ...
x: Union[Object1, Object2, Object3]
if x.key is Key.A:
reveal_type(x) # N: Revealed type is "Union[__main__.Object1, __main__.Object2]"
else:
reveal_type(x) # N: Revealed type is "__main__.Object3"
[builtins fixtures/property.pyi]
[case testNarrowingParentWithAny]
from enum import Enum
from typing import Union, Any
from typing_extensions import Literal
class Key(Enum):
A = 1
B = 2
C = 3
class Object1:
key: Literal[Key.A]
class Object2:
key: Literal[Key.B]
x: Union[Object1, Object2, Any]
if x.key is Key.A:
reveal_type(x.key) # N: Revealed type is "Literal[__main__.Key.A]"
reveal_type(x) # N: Revealed type is "Union[__main__.Object1, Any]"
else:
# TODO: Is this a bug? Should we skip inferring Any for singleton types?
reveal_type(x.key) # N: Revealed type is "Union[Any, Literal[__main__.Key.B]]"
reveal_type(x) # N: Revealed type is "Union[__main__.Object1, __main__.Object2, Any]"
[builtins fixtures/tuple.pyi]
[case testNarrowingParentsHierarchy]
from typing import Union
from typing_extensions import Literal
from enum import Enum
class Key(Enum):
A = 1
B = 2
C = 3
class Parent1:
child: Union[Child1, Child2]
class Parent2:
child: Union[Child2, Child3]
class Parent3:
child: Union[Child3, Child1]
class Child1:
main: Literal[Key.A]
same_for_1_and_2: Literal[Key.A]
class Child2:
main: Literal[Key.B]
same_for_1_and_2: Literal[Key.A]
class Child3:
main: Literal[Key.C]
same_for_1_and_2: Literal[Key.B]
x: Union[Parent1, Parent2, Parent3]
if x.child.main is Key.A:
reveal_type(x) # N: Revealed type is "Union[__main__.Parent1, __main__.Parent3]"
reveal_type(x.child) # N: Revealed type is "__main__.Child1"
else:
reveal_type(x) # N: Revealed type is "Union[__main__.Parent1, __main__.Parent2, __main__.Parent3]"
reveal_type(x.child) # N: Revealed type is "Union[__main__.Child2, __main__.Child3]"
if x.child.same_for_1_and_2 is Key.A:
reveal_type(x) # N: Revealed type is "Union[__main__.Parent1, __main__.Parent2, __main__.Parent3]"
reveal_type(x.child) # N: Revealed type is "Union[__main__.Child1, __main__.Child2]"
else:
reveal_type(x) # N: Revealed type is "Union[__main__.Parent2, __main__.Parent3]"
reveal_type(x.child) # N: Revealed type is "__main__.Child3"
y: Union[Parent1, Parent2]
if y.child.main is Key.A:
reveal_type(y) # N: Revealed type is "__main__.Parent1"
reveal_type(y.child) # N: Revealed type is "__main__.Child1"
else:
reveal_type(y) # N: Revealed type is "Union[__main__.Parent1, __main__.Parent2]"
reveal_type(y.child) # N: Revealed type is "Union[__main__.Child2, __main__.Child3]"
if y.child.same_for_1_and_2 is Key.A:
reveal_type(y) # N: Revealed type is "Union[__main__.Parent1, __main__.Parent2]"
reveal_type(y.child) # N: Revealed type is "Union[__main__.Child1, __main__.Child2]"
else:
reveal_type(y) # N: Revealed type is "__main__.Parent2"
reveal_type(y.child) # N: Revealed type is "__main__.Child3"
[builtins fixtures/tuple.pyi]
[case testNarrowingParentsHierarchyGenerics]
from typing import Generic, TypeVar, Union
T = TypeVar('T')
class Model(Generic[T]):
attr: T
class A:
model: Model[int]
class B:
model: Model[str]
x: Union[A, B]
if isinstance(x.model.attr, int):
reveal_type(x) # N: Revealed type is "__main__.A"
reveal_type(x.model) # N: Revealed type is "__main__.Model[builtins.int]"
else:
reveal_type(x) # N: Revealed type is "__main__.B"
reveal_type(x.model) # N: Revealed type is "__main__.Model[builtins.str]"
[builtins fixtures/isinstance.pyi]
[case testNarrowingParentsHierarchyTypedDict]
# flags: --warn-unreachable
from typing import Union
from typing_extensions import TypedDict, Literal
from enum import Enum
class Key(Enum):
A = 1
B = 2
C = 3
class Parent1(TypedDict):
model: Model1
foo: int
class Parent2(TypedDict):
model: Model2
bar: str
class Model1(TypedDict):
key: Literal[Key.A]
class Model2(TypedDict):
key: Literal[Key.B]
x: Union[Parent1, Parent2]
if x["model"]["key"] is Key.A:
reveal_type(x) # N: Revealed type is "TypedDict('__main__.Parent1', {'model': TypedDict('__main__.Model1', {'key': Literal[__main__.Key.A]}), 'foo': builtins.int})"
reveal_type(x["model"]) # N: Revealed type is "TypedDict('__main__.Model1', {'key': Literal[__main__.Key.A]})"
else:
reveal_type(x) # N: Revealed type is "TypedDict('__main__.Parent2', {'model': TypedDict('__main__.Model2', {'key': Literal[__main__.Key.B]}), 'bar': builtins.str})"
reveal_type(x["model"]) # N: Revealed type is "TypedDict('__main__.Model2', {'key': Literal[__main__.Key.B]})"
y: Union[Parent1, Parent2]
if y["model"]["key"] is Key.C:
reveal_type(y) # E: Statement is unreachable
reveal_type(y["model"])
else:
reveal_type(y) # N: Revealed type is "Union[TypedDict('__main__.Parent1', {'model': TypedDict('__main__.Model1', {'key': Literal[__main__.Key.A]}), 'foo': builtins.int}), TypedDict('__main__.Parent2', {'model': TypedDict('__main__.Model2', {'key': Literal[__main__.Key.B]}), 'bar': builtins.str})]"
reveal_type(y["model"]) # N: Revealed type is "Union[TypedDict('__main__.Model1', {'key': Literal[__main__.Key.A]}), TypedDict('__main__.Model2', {'key': Literal[__main__.Key.B]})]"
[builtins fixtures/tuple.pyi]
[case testNarrowingParentsHierarchyTypedDictWithStr]
# flags: --warn-unreachable
from typing import Union
from typing_extensions import TypedDict, Literal
class Parent1(TypedDict):
model: Model1
foo: int
class Parent2(TypedDict):
model: Model2
bar: str
class Model1(TypedDict):
key: Literal['A']
class Model2(TypedDict):
key: Literal['B']
x: Union[Parent1, Parent2]
if x["model"]["key"] == 'A':
reveal_type(x) # N: Revealed type is "TypedDict('__main__.Parent1', {'model': TypedDict('__main__.Model1', {'key': Literal['A']}), 'foo': builtins.int})"
reveal_type(x["model"]) # N: Revealed type is "TypedDict('__main__.Model1', {'key': Literal['A']})"
else:
reveal_type(x) # N: Revealed type is "TypedDict('__main__.Parent2', {'model': TypedDict('__main__.Model2', {'key': Literal['B']}), 'bar': builtins.str})"
reveal_type(x["model"]) # N: Revealed type is "TypedDict('__main__.Model2', {'key': Literal['B']})"
y: Union[Parent1, Parent2]
if y["model"]["key"] == 'C':
reveal_type(y) # E: Statement is unreachable
reveal_type(y["model"])
else:
reveal_type(y) # N: Revealed type is "Union[TypedDict('__main__.Parent1', {'model': TypedDict('__main__.Model1', {'key': Literal['A']}), 'foo': builtins.int}), TypedDict('__main__.Parent2', {'model': TypedDict('__main__.Model2', {'key': Literal['B']}), 'bar': builtins.str})]"
reveal_type(y["model"]) # N: Revealed type is "Union[TypedDict('__main__.Model1', {'key': Literal['A']}), TypedDict('__main__.Model2', {'key': Literal['B']})]"
[builtins fixtures/primitives.pyi]
[case testNarrowingExprPropagation]
from typing import Union
from typing_extensions import Literal
class A:
tag: Literal['A']
class B:
tag: Literal['B']
abo: Union[A, B, None]
if abo is not None and abo.tag == "A":
reveal_type(abo.tag) # N: Revealed type is "Literal['A']"
reveal_type(abo) # N: Revealed type is "__main__.A"
if not (abo is None or abo.tag != "B"):
reveal_type(abo.tag) # N: Revealed type is "Literal['B']"
reveal_type(abo) # N: Revealed type is "__main__.B"
[builtins fixtures/primitives.pyi]
[case testNarrowingEqualityFlipFlop]
# flags: --warn-unreachable --strict-equality
from typing_extensions import Literal, Final
from enum import Enum
class State(Enum):
A = 1
B = 2
class FlipFlopEnum:
def __init__(self) -> None:
self.state = State.A
def mutate(self) -> None:
self.state = State.B if self.state == State.A else State.A
class FlipFlopStr:
def __init__(self) -> None:
self.state = "state-1"
def mutate(self) -> None:
self.state = "state-2" if self.state == "state-1" else "state-1"
def test1(switch: FlipFlopStr) -> None:
# Naively, we might assume the 'assert' here would narrow the type to
# Literal["state-1"]. However, doing this ends up breaking a fair number of real-world
# code (usually test cases) that looks similar to this function: e.g. checks
# to make sure a field was mutated to some particular value.
#
# And since mypy can't really reason about state mutation, we take a conservative
# approach and avoid narrowing anything here.
assert switch.state == "state-1"
reveal_type(switch.state) # N: Revealed type is "builtins.str"
switch.mutate()
assert switch.state == "state-2"
reveal_type(switch.state) # N: Revealed type is "builtins.str"
def test2(switch: FlipFlopEnum) -> None:
# This is the same thing as 'test1', except we use enums, which we allow to be narrowed
# to literals.
assert switch.state == State.A
reveal_type(switch.state) # N: Revealed type is "Literal[__main__.State.A]"
switch.mutate()
assert switch.state == State.B # E: Non-overlapping equality check (left operand type: "Literal[State.A]", right operand type: "Literal[State.B]")
reveal_type(switch.state) # E: Statement is unreachable
def test3(switch: FlipFlopEnum) -> None:
# Same thing, but using 'is' comparisons. Previously mypy's behaviour differed
# here, narrowing when using 'is', but not when using '=='.
assert switch.state is State.A
reveal_type(switch.state) # N: Revealed type is "Literal[__main__.State.A]"
switch.mutate()
assert switch.state is State.B # E: Non-overlapping identity check (left operand type: "Literal[State.A]", right operand type: "Literal[State.B]")
reveal_type(switch.state) # E: Statement is unreachable
[builtins fixtures/primitives.pyi]
[case testNarrowingEqualityRequiresExplicitStrLiteral]
from typing_extensions import Literal, Final
A_final: Final = "A"
A_literal: Literal["A"]
# Neither the LHS nor the RHS are explicit literals, so regrettably nothing
# is narrowed here -- see 'testNarrowingEqualityFlipFlop' for an example of
# why more precise inference here is problematic.
x_str: str
if x_str == "A":
reveal_type(x_str) # N: Revealed type is "builtins.str"
else:
reveal_type(x_str) # N: Revealed type is "builtins.str"
reveal_type(x_str) # N: Revealed type is "builtins.str"
if x_str == A_final:
reveal_type(x_str) # N: Revealed type is "builtins.str"
else:
reveal_type(x_str) # N: Revealed type is "builtins.str"
reveal_type(x_str) # N: Revealed type is "builtins.str"
# But the RHS is a literal, so we can at least narrow the 'if' case now.
if x_str == A_literal:
reveal_type(x_str) # N: Revealed type is "Literal['A']"
else:
reveal_type(x_str) # N: Revealed type is "builtins.str"
reveal_type(x_str) # N: Revealed type is "builtins.str"
# But in these two cases, the LHS is a literal/literal-like type. So we
# assume the user *does* want literal-based narrowing and narrow accordingly
# regardless of whether the RHS is an explicit literal or not.
x_union: Literal["A", "B", None]
if x_union == A_final:
reveal_type(x_union) # N: Revealed type is "Literal['A']"
else:
reveal_type(x_union) # N: Revealed type is "Union[Literal['B'], None]"
reveal_type(x_union) # N: Revealed type is "Union[Literal['A'], Literal['B'], None]"
if x_union == A_literal:
reveal_type(x_union) # N: Revealed type is "Literal['A']"
else:
reveal_type(x_union) # N: Revealed type is "Union[Literal['B'], None]"
reveal_type(x_union) # N: Revealed type is "Union[Literal['A'], Literal['B'], None]"
[builtins fixtures/primitives.pyi]
[case testNarrowingEqualityRequiresExplicitEnumLiteral]
from typing import Union
from typing_extensions import Literal, Final
from enum import Enum
class Foo(Enum):
A = 1
B = 2
A_final: Final = Foo.A
A_literal: Literal[Foo.A]
# Note this is unlike testNarrowingEqualityRequiresExplicitStrLiteral
# See also testNarrowingEqualityFlipFlop
x1: Foo
if x1 == Foo.A:
reveal_type(x1) # N: Revealed type is "Literal[__main__.Foo.A]"
else:
reveal_type(x1) # N: Revealed type is "Literal[__main__.Foo.B]"
x2: Foo
if x2 == A_final:
reveal_type(x2) # N: Revealed type is "Literal[__main__.Foo.A]"
else:
reveal_type(x2) # N: Revealed type is "Literal[__main__.Foo.B]"
# But we let this narrow since there's an explicit literal in the RHS.
x3: Foo
if x3 == A_literal:
reveal_type(x3) # N: Revealed type is "Literal[__main__.Foo.A]"
else:
reveal_type(x3) # N: Revealed type is "Literal[__main__.Foo.B]"
class SingletonFoo(Enum):
A = "A"
def bar(x: Union[SingletonFoo, Foo], y: SingletonFoo) -> None:
if x == y:
reveal_type(x) # N: Revealed type is "Literal[__main__.SingletonFoo.A]"
[builtins fixtures/primitives.pyi]
[case testNarrowingEqualityDisabledForCustomEquality]
from typing import Union
from typing_extensions import Literal
from enum import Enum
class Custom:
def __eq__(self, other: object) -> bool: return True
class Default: pass
x1: Union[Custom, Literal[1], Literal[2]]
if x1 == 1:
reveal_type(x1) # N: Revealed type is "Union[__main__.Custom, Literal[1], Literal[2]]"
else:
reveal_type(x1) # N: Revealed type is "Union[__main__.Custom, Literal[1], Literal[2]]"
x2: Union[Default, Literal[1], Literal[2]]
if x2 == 1:
reveal_type(x2) # N: Revealed type is "Literal[1]"
else:
reveal_type(x2) # N: Revealed type is "Union[__main__.Default, Literal[2]]"
class CustomEnum(Enum):
A = 1
B = 2
def __eq__(self, other: object) -> bool: return True
x3: CustomEnum
key: Literal[CustomEnum.A]
if x3 == key:
reveal_type(x3) # N: Revealed type is "__main__.CustomEnum"
else:
reveal_type(x3) # N: Revealed type is "__main__.CustomEnum"
# For comparison, this narrows since we bypass __eq__
if x3 is key:
reveal_type(x3) # N: Revealed type is "Literal[__main__.CustomEnum.A]"
else:
reveal_type(x3) # N: Revealed type is "Literal[__main__.CustomEnum.B]"
[builtins fixtures/primitives.pyi]
[case testNarrowingEqualityDisabledForCustomEqualityChain]
# flags: --strict-equality --warn-unreachable
from typing import Union
from typing_extensions import Literal
class Custom:
def __eq__(self, other: object) -> bool: return True
class Default: pass
x: Literal[1, 2, None]
y: Custom
z: Default
# We could maybe try doing something clever, but for simplicity we
# treat the whole chain as contaminated and mostly disable narrowing.
#
# The only exception is that we do at least strip away the 'None'. We
# (perhaps optimistically) assume no custom class would be pathological
# enough to declare itself to be equal to None and so permit this narrowing,
# since it's often convenient in practice.
if 1 == x == y:
reveal_type(x) # N: Revealed type is "Union[Literal[1], Literal[2]]"
reveal_type(y) # N: Revealed type is "__main__.Custom"
else:
reveal_type(x) # N: Revealed type is "Union[Literal[1], Literal[2], None]"
reveal_type(y) # N: Revealed type is "__main__.Custom"
# No contamination here
if 1 == x == z: # E: Non-overlapping equality check (left operand type: "Optional[Literal[1, 2]]", right operand type: "Default")
reveal_type(x) # E: Statement is unreachable
reveal_type(z)
else:
reveal_type(x) # N: Revealed type is "Union[Literal[1], Literal[2], None]"
reveal_type(z) # N: Revealed type is "__main__.Default"
[builtins fixtures/primitives.pyi]
[case testNarrowingUnreachableCases]
# flags: --strict-equality --warn-unreachable
from typing import Union
from typing_extensions import Literal
a: Literal[1]
b: Literal[1, 2]
c: Literal[2, 3]
if a == b == c:
reveal_type(a) # E: Statement is unreachable
reveal_type(b)
reveal_type(c)
else:
reveal_type(a) # N: Revealed type is "Literal[1]"
reveal_type(b) # N: Revealed type is "Union[Literal[1], Literal[2]]"
reveal_type(c) # N: Revealed type is "Union[Literal[2], Literal[3]]"
if a == a == a:
reveal_type(a) # N: Revealed type is "Literal[1]"
else:
reveal_type(a) # E: Statement is unreachable
if a == a == b:
reveal_type(a) # N: Revealed type is "Literal[1]"
reveal_type(b) # N: Revealed type is "Literal[1]"
else:
reveal_type(a) # N: Revealed type is "Literal[1]"
reveal_type(b) # N: Revealed type is "Literal[2]"
# In this case, it's ok for 'b' to narrow down to Literal[1] in the else case
# since that's the only way 'b == 2' can be false
if b == 2:
reveal_type(b) # N: Revealed type is "Literal[2]"
else:
reveal_type(b) # N: Revealed type is "Literal[1]"
# But in this case, we can't conclude anything about the else case. This expression
# could end up being either '2 == 2 == 3' or '1 == 2 == 2', which means we can't
# conclude anything.
if b == 2 == c:
reveal_type(b) # N: Revealed type is "Literal[2]"
reveal_type(c) # N: Revealed type is "Literal[2]"
else:
reveal_type(b) # N: Revealed type is "Union[Literal[1], Literal[2]]"
reveal_type(c) # N: Revealed type is "Union[Literal[2], Literal[3]]"
[builtins fixtures/primitives.pyi]
[case testNarrowingUnreachableCases2]
# flags: --strict-equality --warn-unreachable
from typing import Union
from typing_extensions import Literal
a: Literal[1, 2, 3, 4]
b: Literal[1, 2, 3, 4]
if a == b == 1:
reveal_type(a) # N: Revealed type is "Literal[1]"
reveal_type(b) # N: Revealed type is "Literal[1]"
elif a == b == 2:
reveal_type(a) # N: Revealed type is "Literal[2]"
reveal_type(b) # N: Revealed type is "Literal[2]"
elif a == b == 3:
reveal_type(a) # N: Revealed type is "Literal[3]"
reveal_type(b) # N: Revealed type is "Literal[3]"
elif a == b == 4:
reveal_type(a) # N: Revealed type is "Literal[4]"
reveal_type(b) # N: Revealed type is "Literal[4]"
else:
# This branch is reachable if a == 1 and b == 2, for example.
reveal_type(a) # N: Revealed type is "Union[Literal[1], Literal[2], Literal[3], Literal[4]]"
reveal_type(b) # N: Revealed type is "Union[Literal[1], Literal[2], Literal[3], Literal[4]]"
if a == a == 1:
reveal_type(a) # N: Revealed type is "Literal[1]"
elif a == a == 2:
reveal_type(a) # N: Revealed type is "Literal[2]"
elif a == a == 3:
reveal_type(a) # N: Revealed type is "Literal[3]"
elif a == a == 4:
reveal_type(a) # N: Revealed type is "Literal[4]"
else:
# In contrast, this branch must be unreachable: we assume (maybe naively)
# that 'a' won't be mutated in the middle of the expression.
reveal_type(a) # E: Statement is unreachable
reveal_type(b)
[builtins fixtures/primitives.pyi]
[case testNarrowingLiteralTruthiness]
from typing import Union
from typing_extensions import Literal
str_or_false: Union[Literal[False], str]
if str_or_false:
reveal_type(str_or_false) # N: Revealed type is "builtins.str"
else:
reveal_type(str_or_false) # N: Revealed type is "Union[Literal[False], builtins.str]"
true_or_false: Literal[True, False]
if true_or_false:
reveal_type(true_or_false) # N: Revealed type is "Literal[True]"
else:
reveal_type(true_or_false) # N: Revealed type is "Literal[False]"
[builtins fixtures/primitives.pyi]
[case testNarrowingLiteralIdentityCheck]
from typing import Union
from typing_extensions import Literal
str_or_false: Union[Literal[False], str]
if str_or_false is not False:
reveal_type(str_or_false) # N: Revealed type is "builtins.str"
else:
reveal_type(str_or_false) # N: Revealed type is "Literal[False]"
if str_or_false is False:
reveal_type(str_or_false) # N: Revealed type is "Literal[False]"
else:
reveal_type(str_or_false) # N: Revealed type is "builtins.str"
str_or_true: Union[Literal[True], str]
if str_or_true is True:
reveal_type(str_or_true) # N: Revealed type is "Literal[True]"
else:
reveal_type(str_or_true) # N: Revealed type is "builtins.str"
if str_or_true is not True:
reveal_type(str_or_true) # N: Revealed type is "builtins.str"
else:
reveal_type(str_or_true) # N: Revealed type is "Literal[True]"
str_or_bool_literal: Union[Literal[False], Literal[True], str]
if str_or_bool_literal is not True:
reveal_type(str_or_bool_literal) # N: Revealed type is "Union[Literal[False], builtins.str]"
else:
reveal_type(str_or_bool_literal) # N: Revealed type is "Literal[True]"
if str_or_bool_literal is not True and str_or_bool_literal is not False:
reveal_type(str_or_bool_literal) # N: Revealed type is "builtins.str"
else:
reveal_type(str_or_bool_literal) # N: Revealed type is "builtins.bool"
[builtins fixtures/primitives.pyi]
[case testNarrowingBooleanIdentityCheck]
from typing import Optional
from typing_extensions import Literal
bool_val: bool
if bool_val is not False:
reveal_type(bool_val) # N: Revealed type is "Literal[True]"
else:
reveal_type(bool_val) # N: Revealed type is "Literal[False]"
opt_bool_val: Optional[bool]
if opt_bool_val is not None:
reveal_type(opt_bool_val) # N: Revealed type is "builtins.bool"
if opt_bool_val is not False:
reveal_type(opt_bool_val) # N: Revealed type is "Union[Literal[True], None]"
else:
reveal_type(opt_bool_val) # N: Revealed type is "Literal[False]"
[builtins fixtures/primitives.pyi]
[case testNarrowingBooleanTruthiness]
from typing import Optional
from typing_extensions import Literal
bool_val: bool
if bool_val:
reveal_type(bool_val) # N: Revealed type is "Literal[True]"
else:
reveal_type(bool_val) # N: Revealed type is "Literal[False]"
reveal_type(bool_val) # N: Revealed type is "builtins.bool"
opt_bool_val: Optional[bool]
if opt_bool_val:
reveal_type(opt_bool_val) # N: Revealed type is "Literal[True]"
else:
reveal_type(opt_bool_val) # N: Revealed type is "Union[Literal[False], None]"
reveal_type(opt_bool_val) # N: Revealed type is "Union[builtins.bool, None]"
[builtins fixtures/primitives.pyi]
[case testNarrowingBooleanBoolOp]
from typing import Optional
from typing_extensions import Literal
bool_a: bool
bool_b: bool
if bool_a and bool_b:
reveal_type(bool_a) # N: Revealed type is "Literal[True]"
reveal_type(bool_b) # N: Revealed type is "Literal[True]"
else:
reveal_type(bool_a) # N: Revealed type is "builtins.bool"
reveal_type(bool_b) # N: Revealed type is "builtins.bool"
if not bool_a or bool_b:
reveal_type(bool_a) # N: Revealed type is "builtins.bool"
reveal_type(bool_b) # N: Revealed type is "builtins.bool"
else:
reveal_type(bool_a) # N: Revealed type is "Literal[True]"
reveal_type(bool_b) # N: Revealed type is "Literal[False]"
if True and bool_b:
reveal_type(bool_b) # N: Revealed type is "Literal[True]"
x = True and bool_b
reveal_type(x) # N: Revealed type is "builtins.bool"
[builtins fixtures/primitives.pyi]
[case testNarrowingTypedDictUsingEnumLiteral]
from typing import Union
from typing_extensions import TypedDict, Literal
from enum import Enum
class E(Enum):
FOO = "a"
BAR = "b"
class Foo(TypedDict):
tag: Literal[E.FOO]
x: int
class Bar(TypedDict):
tag: Literal[E.BAR]
y: int
def f(d: Union[Foo, Bar]) -> None:
assert d['tag'] == E.FOO
d['x']
reveal_type(d) # N: Revealed type is "TypedDict('__main__.Foo', {'tag': Literal[__main__.E.FOO], 'x': builtins.int})"
[builtins fixtures/dict.pyi]
[case testNarrowingUsingMetaclass]
from typing import Type
class M(type):
pass
class C: pass
def f(t: Type[C]) -> None:
if type(t) is M:
reveal_type(t) # N: Revealed type is "Type[__main__.C]"
else:
reveal_type(t) # N: Revealed type is "Type[__main__.C]"
if type(t) is not M:
reveal_type(t) # N: Revealed type is "Type[__main__.C]"
else:
reveal_type(t) # N: Revealed type is "Type[__main__.C]"
reveal_type(t) # N: Revealed type is "Type[__main__.C]"
[case testNarrowingUsingTypeVar]
from typing import Type, TypeVar
class A: pass
class B(A): pass
T = TypeVar("T", bound=A)
def f(t: Type[T], a: A, b: B) -> None:
if type(a) is t:
reveal_type(a) # N: Revealed type is "T`-1"
else:
reveal_type(a) # N: Revealed type is "__main__.A"
if type(b) is t:
reveal_type(b) # N: Revealed type is "<nothing>"
else:
reveal_type(b) # N: Revealed type is "__main__.B"
[case testNarrowingNestedUnionOfTypedDicts]
from typing import Union
from typing_extensions import Literal, TypedDict
class A(TypedDict):
tag: Literal["A"]
a: int
class B(TypedDict):
tag: Literal["B"]
b: int
class C(TypedDict):
tag: Literal["C"]
c: int
AB = Union[A, B]
ABC = Union[AB, C]
abc: ABC
if abc["tag"] == "A":
reveal_type(abc) # N: Revealed type is "TypedDict('__main__.A', {'tag': Literal['A'], 'a': builtins.int})"
elif abc["tag"] == "C":
reveal_type(abc) # N: Revealed type is "TypedDict('__main__.C', {'tag': Literal['C'], 'c': builtins.int})"
else:
reveal_type(abc) # N: Revealed type is "TypedDict('__main__.B', {'tag': Literal['B'], 'b': builtins.int})"
[builtins fixtures/primitives.pyi]
[case testNarrowingRuntimeCover]
from typing import Dict, List, Union
def unreachable(x: Union[str, List[str]]) -> None:
if isinstance(x, str):
reveal_type(x) # N: Revealed type is "builtins.str"
elif isinstance(x, list):
reveal_type(x) # N: Revealed type is "builtins.list[builtins.str]"
else:
reveal_type(x) # No output: this branch is unreachable
def all_parts_covered(x: Union[str, List[str], List[int], int]) -> None:
if isinstance(x, str):
reveal_type(x) # N: Revealed type is "builtins.str"
elif isinstance(x, list):
reveal_type(x) # N: Revealed type is "Union[builtins.list[builtins.str], builtins.list[builtins.int]]"
else:
reveal_type(x) # N: Revealed type is "builtins.int"
def two_type_vars(x: Union[str, Dict[str, int], Dict[bool, object], int]) -> None:
if isinstance(x, str):
reveal_type(x) # N: Revealed type is "builtins.str"
elif isinstance(x, dict):
reveal_type(x) # N: Revealed type is "Union[builtins.dict[builtins.str, builtins.int], builtins.dict[builtins.bool, builtins.object]]"
else:
reveal_type(x) # N: Revealed type is "builtins.int"
[builtins fixtures/dict.pyi]
[case testNarrowingWithDef]
from typing import Callable, Optional
def g() -> None:
foo: Optional[Callable[[], None]] = None
if foo is None:
def foo(): ...
foo()
[builtins fixtures/dict.pyi]
[case testNarrowingOptionalEqualsNone]
from typing import Optional
class A: ...
val: Optional[A]
if val == None:
reveal_type(val) # N: Revealed type is "Union[__main__.A, None]"
else:
reveal_type(val) # N: Revealed type is "Union[__main__.A, None]"
if val != None:
reveal_type(val) # N: Revealed type is "Union[__main__.A, None]"
else:
reveal_type(val) # N: Revealed type is "Union[__main__.A, None]"
if val in (None,):
reveal_type(val) # N: Revealed type is "Union[__main__.A, None]"
else:
reveal_type(val) # N: Revealed type is "Union[__main__.A, None]"
if val not in (None,):
reveal_type(val) # N: Revealed type is "Union[__main__.A, None]"
else:
reveal_type(val) # N: Revealed type is "Union[__main__.A, None]"
[builtins fixtures/primitives.pyi]
[case testNarrowingWithTupleOfTypes]
from typing import Tuple, Type
class Base: ...
class Impl1(Base): ...
class Impl2(Base): ...
impls: Tuple[Type[Base], ...] = (Impl1, Impl2)
some: object
if isinstance(some, impls):
reveal_type(some) # N: Revealed type is "__main__.Base"
else:
reveal_type(some) # N: Revealed type is "builtins.object"
raw: Tuple[type, ...]
if isinstance(some, raw):
reveal_type(some) # N: Revealed type is "builtins.object"
else:
reveal_type(some) # N: Revealed type is "builtins.object"
[builtins fixtures/dict.pyi]
[case testNarrowingWithTupleOfTypesPy310Plus]
# flags: --python-version 3.10
class Base: ...
class Impl1(Base): ...
class Impl2(Base): ...
some: int | Base
impls: tuple[type[Base], ...] = (Impl1, Impl2)
if isinstance(some, impls):
reveal_type(some) # N: Revealed type is "__main__.Base"
else:
reveal_type(some) # N: Revealed type is "Union[builtins.int, __main__.Base]"
raw: tuple[type, ...]
if isinstance(some, raw):
reveal_type(some) # N: Revealed type is "Union[builtins.int, __main__.Base]"
else:
reveal_type(some) # N: Revealed type is "Union[builtins.int, __main__.Base]"
[builtins fixtures/dict.pyi]