Skip to content

Commit 6a18078

Browse files
committed
Implement TypeVar support in narrowing
If a TypeVar with a TypedDict upper bound is encountered while narrowing, narrow based on the upper bound.
1 parent 0a838cc commit 6a18078

2 files changed

Lines changed: 27 additions & 10 deletions

File tree

mypy/checker.py

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6391,7 +6391,8 @@ def conditional_types_for_iterable(
63916391
) -> tuple[Type, Type]:
63926392
"""
63936393
Narrows the type of `iterable_type` based on the type of `item_type`.
6394-
For now, we only support narrowing unions of TypedDicts based on left operand being literal string(s).
6394+
For now, we only support narrowing unions of TypedDicts, and TypeVars with TypedDict
6395+
bounds, based on left operand being literal string(s).
63956396
"""
63966397
if_types: list[Type] = []
63976398
else_types: list[Type] = []
@@ -6405,18 +6406,19 @@ def conditional_types_for_iterable(
64056406
item_str_literals = try_getting_str_literals_from_type(item_type)
64066407

64076408
for possible_iterable_type in possible_iterable_types:
6408-
if item_str_literals and isinstance(possible_iterable_type, TypedDictType):
6409+
bound = (
6410+
get_proper_type(possible_iterable_type.upper_bound)
6411+
if isinstance(possible_iterable_type, TypeVarType)
6412+
else possible_iterable_type
6413+
)
6414+
6415+
if item_str_literals and isinstance(bound, TypedDictType):
64096416
for key in item_str_literals:
6410-
if key in possible_iterable_type.required_keys:
6417+
if key in bound.required_keys:
64116418
if_types.append(possible_iterable_type)
64126419
elif (
6413-
key in possible_iterable_type.items
6414-
and not isinstance(possible_iterable_type.items[key], UninhabitedType)
6415-
) or (
6416-
key not in possible_iterable_type.items
6417-
and not possible_iterable_type.is_closed
6418-
and not possible_iterable_type.is_final
6419-
):
6420+
key in bound.items and not isinstance(bound.items[key], UninhabitedType)
6421+
) or (key not in bound.items and not bound.is_closed and not bound.is_final):
64206422
if_types.append(possible_iterable_type)
64216423
else_types.append(possible_iterable_type)
64226424
else:

test-data/unit/check-typeddict.test

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5527,6 +5527,21 @@ else:
55275527
[builtins fixtures/dict.pyi]
55285528
[typing fixtures/typing-full.pyi]
55295529

5530+
[case testOperatorContainsNarrowsTypeVarWithClosedTypedDictBound]
5531+
from typing import TypedDict, TypeVar
5532+
from typing_extensions import Never, assert_type
5533+
5534+
T = TypeVar('T', bound='D')
5535+
5536+
class D(TypedDict, closed=True):
5537+
a: int
5538+
5539+
def func(t: T):
5540+
if "b" in t:
5541+
assert_type(t, Never)
5542+
[builtins fixtures/dict.pyi]
5543+
[typing fixtures/typing-typeddict.pyi]
5544+
55305545

55315546
[case testTypedDictFinalAndClassVar]
55325547
from typing import TypedDict, Final, ClassVar

0 commit comments

Comments
 (0)