Skip to content

Commit 0729345

Browse files
authored
Fix narrowing down TypedDict unions with enum literal types (#10415)
A union of enum literals was merged back to the enum type, which broke type narrowing. Disable merging in this case to work around the issue. The fix feels a bit ad hoc. However, I'd rather not spend a lot of time figuring out a general fix, since this seems like a pretty rare edge case. Fixes #10414.
1 parent b17e33f commit 0729345

File tree

2 files changed

+26
-1
lines changed

2 files changed

+26
-1
lines changed

mypy/checkexpr.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2910,9 +2910,11 @@ def visit_index_with_type(self, left_type: Type, e: IndexExpr,
29102910

29112911
if isinstance(left_type, UnionType):
29122912
original_type = original_type or left_type
2913+
# Don't combine literal types, since we may need them for type narrowing.
29132914
return make_simplified_union([self.visit_index_with_type(typ, e,
29142915
original_type)
2915-
for typ in left_type.relevant_items()])
2916+
for typ in left_type.relevant_items()],
2917+
contract_literals=False)
29162918
elif isinstance(left_type, TupleType) and self.chk.in_checked_function():
29172919
# Special case for tuples. They return a more specific type when
29182920
# indexed by an integer literal.

test-data/unit/check-narrowing.test

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1029,3 +1029,26 @@ else:
10291029
reveal_type(str_or_bool_literal) # N: Revealed type is "Union[Literal[False], Literal[True]]"
10301030

10311031
[builtins fixtures/primitives.pyi]
1032+
1033+
[case testNarrowingTypedDictUsingEnumLiteral]
1034+
from typing import Union
1035+
from typing_extensions import TypedDict, Literal
1036+
from enum import Enum
1037+
1038+
class E(Enum):
1039+
FOO = "a"
1040+
BAR = "b"
1041+
1042+
class Foo(TypedDict):
1043+
tag: Literal[E.FOO]
1044+
x: int
1045+
1046+
class Bar(TypedDict):
1047+
tag: Literal[E.BAR]
1048+
y: int
1049+
1050+
def f(d: Union[Foo, Bar]) -> None:
1051+
assert d['tag'] == E.FOO
1052+
d['x']
1053+
reveal_type(d) # N: Revealed type is "TypedDict('__main__.Foo', {'tag': Literal[__main__.E.FOO], 'x': builtins.int})"
1054+
[builtins fixtures/dict.pyi]

0 commit comments

Comments
 (0)