diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 4ed64e590950..6ea58831b53b 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -6212,7 +6212,13 @@ def accept( column=node_as_type.column, is_type_form=True, ) - if is_subtype(typ1, p_type_context): + # Type variables in the TypeForm branch can prevent the subtype + # check from recognizing a type expression. Erase them only for + # this check; normal argument checking and inference still use + # the original context. + if is_subtype(typ1, p_type_context) or is_subtype( + typ1, erasetype.erase_typevars(p_type_context) + ): typ = typ1 # r-value type, when interpreted as a type expression else: typ2 = node.accept(self) diff --git a/test-data/unit/check-typeform.test b/test-data/unit/check-typeform.test index d16c54a06b66..7c7560213456 100644 --- a/test-data/unit/check-typeform.test +++ b/test-data/unit/check-typeform.test @@ -255,6 +255,15 @@ typx2: TypeForm = no_such_module.NoSuchType # E: Name "no_such_module" is not d -- Type Expression Context: Union[TypeForm, ] +[case testCanPassTypeExpressionToTypeFormInUnionParameter] +from typing import TypeVar +from typing_extensions import Annotated, TypeForm +T = TypeVar('T') +def f(typx: TypeForm[T] | None) -> T: ... +reveal_type(f(Annotated[int, 'meta'])) # N: Revealed type is "builtins.int" +[builtins fixtures/primitives.pyi] +[typing fixtures/typing-full.pyi] + [case testAcceptsTypeFormLiteralAssignedToUnionOfTypeFormAndNonStr] from typing_extensions import TypeForm typx_or_int1: TypeForm[int | None] | int = int | None # No error; interpret as TypeForm