Skip to content

Commit bd05e88

Browse files
committed
Allow adding Never items to closed subtypes
A closed subtype can validly add a key, provided it is uninhabited, as this is just explicitly stating the previously implicit constraint.
1 parent 022a501 commit bd05e88

2 files changed

Lines changed: 24 additions & 3 deletions

File tree

mypy/subtypes.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -941,9 +941,6 @@ def visit_typeddict_type(self, left: TypedDictType) -> bool:
941941

942942
# Perform fast key-based checks before recursing into value types
943943
for name, l, r in left.zipall(right):
944-
# New keys cannot be added to a closed supertype
945-
if name not in right.items and right.is_closed:
946-
return False
947944
# Required keys must remain required
948945
if r.required and not l.required:
949946
return False

test-data/unit/check-typeddict.test

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5391,6 +5391,30 @@ fB(b)
53915391
[builtins fixtures/dict.pyi]
53925392
[typing fixtures/typing-typeddict.pyi]
53935393

5394+
[case testTypedDictSubtypingClosedExplicitNever]
5395+
from typing import NotRequired, ReadOnly, TypedDict
5396+
from typing_extensions import Never
5397+
A = TypedDict('A', {}, closed=True)
5398+
B = TypedDict('B', {'x': NotRequired[Never]}, closed=True)
5399+
C = TypedDict('C', {'x': ReadOnly[NotRequired[Never]]}, closed=True)
5400+
def f_a(x: A) -> None: pass
5401+
def f_b(x: B) -> None: pass
5402+
def f_c(x: C) -> None: pass
5403+
a: A
5404+
b: B
5405+
c: C
5406+
# Logically identical types: legal
5407+
f_a(b)
5408+
f_b(a)
5409+
# Subtype allows key removal: legal
5410+
f_c(a)
5411+
f_c(b)
5412+
# Subtype prevents key removal: illegal
5413+
f_a(c) # E: Argument 1 to "f_a" has incompatible type "C"; expected "A"
5414+
f_b(c) # E: Argument 1 to "f_b" has incompatible type "C"; expected "B"
5415+
[builtins fixtures/dict.pyi]
5416+
[typing fixtures/typing-typeddict.pyi]
5417+
53945418
[case testJoinOfClosedTypedDict]
53955419
from typing import Any, TypedDict, TypeVar
53965420
A = TypedDict('A', {'x': int}, closed=True)

0 commit comments

Comments
 (0)