diff --git a/mypy/subtypes.py b/mypy/subtypes.py index 11f3421331a5..a26aaf798b58 100644 --- a/mypy/subtypes.py +++ b/mypy/subtypes.py @@ -910,14 +910,12 @@ def visit_typeddict_type(self, left: TypedDictType) -> bool: return False # Non-required key is not compatible with a required key since # indexing may fail unexpectedly if a required key is missing. - # Required key is not compatible with a non-required key since - # the prior doesn't support 'del' but the latter should support - # it. - # - # NOTE: 'del' support is currently not implemented (#3550). We - # don't want to have to change subtyping after 'del' support - # lands so here we are anticipating that change. - if (name in left.required_keys) != (name in right.required_keys): + # Required key is not compatible with a non-read-only non-required + # key since the prior doesn't support 'del' but the latter should + # support it. + # Required key is compatible with a read-only non-required key. + required_differ = (name in left.required_keys) != (name in right.required_keys) + if not right_readonly and required_differ: return False # Readonly fields check: # diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test index affa472bb640..521a5b80573d 100644 --- a/test-data/unit/check-typeddict.test +++ b/test-data/unit/check-typeddict.test @@ -3894,6 +3894,20 @@ accepts_B(b) [builtins fixtures/dict.pyi] [typing fixtures/typing-typeddict.pyi] +[case testTypedDictRequiredConsistentWithNotRequiredReadOnly] +from typing import NotRequired, ReadOnly, Required, TypedDict + +class A(TypedDict): + x: NotRequired[ReadOnly[str]] + +class B(TypedDict): + x: Required[str] + +def f(b: B): + a: A = b # ok +[builtins fixtures/dict.pyi] +[typing fixtures/typing-typeddict.pyi] + [case testTypedDictReadOnlyCall] from typing import ReadOnly, TypedDict