diff --git a/mypy/meet.py b/mypy/meet.py index 353af59367ad..63305c2bb236 100644 --- a/mypy/meet.py +++ b/mypy/meet.py @@ -170,7 +170,9 @@ def narrow_declared_type(declared: Type, narrowed: Type) -> Type: ): # We put this branch early to get T(bound=Union[A, B]) instead of # Union[T(bound=A), T(bound=B)] that will be confusing for users. - return declared.copy_modified(upper_bound=original_narrowed) + return declared.copy_modified( + upper_bound=narrow_declared_type(declared.upper_bound, original_narrowed) + ) elif not is_overlapping_types(declared, narrowed, prohibit_none_typevar_overlap=True): if state.strict_optional: return UninhabitedType() diff --git a/test-data/unit/check-typeguard.test b/test-data/unit/check-typeguard.test index 93e665e4548c..b15458d5819a 100644 --- a/test-data/unit/check-typeguard.test +++ b/test-data/unit/check-typeguard.test @@ -825,6 +825,26 @@ def handle(model: Model) -> int: return 0 [builtins fixtures/tuple.pyi] +[case testTypeGuardedTypeDoesNotLeakTypeVar] +# flags: --debug-serialize +# https://github.com/python/mypy/issues/20015 +from typing import Generic, TypeVar, TypeGuard + +class A: ... +class B: ... + +def is_a(_: object) -> TypeGuard[A]: return True +def is_b(_: object) -> TypeGuard[B]: return True + +_T = TypeVar("_T") + +class Foo(Generic[_T]): + def __init__(self, v: _T) -> None: + if is_a(v) or is_b(v): + self.v = v +[builtins fixtures/tuple.pyi] +[typing fixtures/typing-full.pyi] + [case testTypeGuardRestrictTypeVarUnion] from typing import Union, TypeVar from typing_extensions import TypeGuard diff --git a/test-data/unit/fixtures/typing-full.pyi b/test-data/unit/fixtures/typing-full.pyi index 3757e868552e..1a63deaa727d 100644 --- a/test-data/unit/fixtures/typing-full.pyi +++ b/test-data/unit/fixtures/typing-full.pyi @@ -34,6 +34,7 @@ no_type_check = 0 ClassVar = 0 Final = 0 TypedDict = 0 +TypeGuard = 0 NoReturn = 0 NewType = 0 Self = 0