Skip to content

Treat widening type variables consistently #18456

Open

Description

@A5rocks

Bug Report

Currently, mypy allows widening type variables without an error, then errors (sometimes, see below) at usage time. This seems backwards to me. I'm opening this based on a minimization from @sterliakov in #18451.

To Reproduce

There's several interesting snippets:

import typing

IntT = typing.TypeVar("IntT", bound=int)
StrT = typing.TypeVar("StrT", bound=str)
AnyT = typing.TypeVar("AnyT", bound=typing.Any)

class IntC(typing.Generic[IntT]): ...
class StrC(typing.Generic[StrT]): ...

AnyC = IntC[AnyT] | StrC[AnyT]

def run(_: AnyC[StrT]) -> StrT: ...  # E: Type argument "StrT" of "IntC" must be a subtype of "int"

In this case, I would expect an error on the AnyC type alias definition, and maybe at usage time. What about other methods of widening type variables?

import typing

IntT = typing.TypeVar("IntT", bound=int)
StrT = typing.TypeVar("StrT", bound=str)
AnyT = typing.TypeVar("AnyT", bound=typing.Any)

class IntC(typing.Generic[IntT]): ...

class AnyC(IntC[AnyT]):  # ! no error
    pass

def f(x: StrT) -> StrT:
    y: AnyC[StrT]  # !? no error
    return x

This version doesn't error at all! What I would expect is the behavior achieved by using object plus a class to widen:

import typing

IntT = typing.TypeVar("IntT", bound=int)
StrT = typing.TypeVar("StrT", bound=str)
AnyT = typing.TypeVar("AnyT", bound=object)

class IntC(typing.Generic[IntT]): ...
class StrC(typing.Generic[StrT]): ...

class AnyC1(IntC[AnyT]):  # E: Type argument "AnyT" of "IntC" must be a subtype of "int"  
    pass

AnyC2 = IntC[AnyT] | StrC[AnyT]  # ! no error

def f(x: StrT) -> StrT:
    y1: AnyC1[StrT]  # no error, this is fine
    y2: AnyC2[StrT]  # E: Type argument "StrT" of "IntC" must be a subtype of "int"
    return x

Expected Behavior

Consistency.

Actual Behavior

To summarize the above

method of widening bound=object bound=Any
type alias usage error usage error
class definition error no errors (!)

Your Environment

Everything was double checked in mypy playground.

  • Mypy version used: v1.14.1
  • Mypy command-line flags: none
  • Mypy configuration options from mypy.ini (and other config files): N/A
  • Python version used: 3.12

Activity

  1. sterliakov commented on Jan 13, 2025

    @sterliakov
    Collaborator

    Perhaps this question is worth a python/typing discussion? It is rather typechecker-agnostic and concerns the meaning of annotations, not a specific implementation.

  2. A5rocks commented on Jan 14, 2025

    @A5rocks
    CollaboratorAuthor

    I think a good enough solution is to ensure that classes error at definition time and type aliases at usage time, based on the level of formality described in #18451. I think the most egregious issue here is that bound=Any typevar that prevents any errors.

    However, changing is_subtype to is_proper_subtype in the relevant place leads to rejecting gradual types. I'm not sure the right approach here... I don't like current behavior but at least across mypy's test cases it's typical -- e.g. Callable[..., Any] instead of Callable[..., object] in a typevar bound (nooo!! why!). Maybe status quo is indeed the best.

  3. added and removed
    bugmypy got something wrong
    on Oct 10, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions