Skip to content

Commit

Permalink
respect final-ity of TypedDict
Browse files Browse the repository at this point in the history
  • Loading branch information
ikonst committed Oct 15, 2022
1 parent 88e2c9f commit f799344
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 6 deletions.
2 changes: 1 addition & 1 deletion mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -5037,7 +5037,7 @@ def _contains_string_right_operand_type_map(
for key in item_strs:
if key in t.required_keys:
if_types.append(t)
elif key in t.items:
elif key in t.items or not t.is_final:
if_types.append(t)
else_types.append(t)
else:
Expand Down
4 changes: 4 additions & 0 deletions mypy/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -2290,6 +2290,10 @@ def deserialize(cls, data: JsonDict) -> TypedDictType:
Instance.deserialize(data["fallback"]),
)

@property
def is_final(self) -> bool:
return self.fallback.type.is_final

def is_anonymous(self) -> bool:
return self.fallback.type.fullname in TPDICT_FB_NAMES

Expand Down
53 changes: 48 additions & 5 deletions test-data/unit/check-typeddict.test
Original file line number Diff line number Diff line change
Expand Up @@ -2017,7 +2017,6 @@ from __future__ import annotations
from typing import assert_type, TypedDict, Union
from typing_extensions import final


@final
class D(TypedDict):
foo: int
Expand All @@ -2030,10 +2029,10 @@ if 'foo' in d:
else:
assert_type(d, list[str])

[builtins fixtures / dict.pyi]
[typing fixtures / typing - typeddict.pyi]
[builtins fixtures/dict.pyi]
[typing fixtures/typing-typeddict.pyi]

[case testOperatorContainsNarrowsTotalTypedDicts]
[case testOperatorContainsNarrowsTypedDicts_total]
from __future__ import annotations
from typing import assert_type, Literal, TypedDict, TypeVar, Union
from typing_extensions import final
Expand All @@ -2048,6 +2047,13 @@ class D2(TypedDict):
bar: int

d: D1 | D2
opt_d: D1 | None

if 'foo' in opt_d:
assert_type(opt_d, D1)
else:
assert_type(opt_d, None)


if 'foo' in d:
assert_type(d, D1)
Expand Down Expand Up @@ -2081,7 +2087,44 @@ def f(arg: TD) -> None:
[builtins fixtures/dict.pyi]
[typing fixtures/typing-typeddict.pyi]

[case testOperatorContainsNarrowsPartialTypedDicts]
[case testOperatorContainsNarrowsTypedDicts_final]
# flags: --warn-unreachable
from __future__ import annotations
from typing import assert_type, Literal, TypedDict, TypeVar, Union
from typing_extensions import final

@final
class DFinal(TypedDict):
foo: int


class DNotFinal(TypedDict):
bar: int


d_not_final: DNotFinal

if 'bar' in d_not_final:
assert_type(d_not_final, DNotFinal)
else:
spam = 'ham' # E: Statement is unreachable

if 'spam' in d_not_final:
assert_type(d_not_final, DNotFinal)
else:
assert_type(d_not_final, DNotFinal)

d_union: DFinal | DNotFinal

if 'foo' in d_union:
assert_type(d_union, Union[DFinal, DNotFinal])
else:
assert_type(d_union, DNotFinal)

[builtins fixtures/dict.pyi]
[typing fixtures/typing-typeddict.pyi]

[case testOperatorContainsNarrowsTypedDicts_partial]
from __future__ import annotations
from typing import assert_type, Literal, TypedDict, Union
from typing_extensions import final
Expand Down

0 comments on commit f799344

Please sign in to comment.