I am not sure I agree with the premise of this question.
Here’s part of the docstring from 3.8
class TypeVar(_Final, _Immutable, _root=True):
"""Type variable.
Usage::
T = TypeVar('T') # Can be anything
A = TypeVar('A', str, bytes) # Must be str or bytes
....
def __init__(self, name, *constraints, bound=None,
covariant=False, contravariant=False):
....
Now, if you had just
ThetaType = TypeVar('ThetaType')
XType = TypeVar('XType')
would you be arguing that uses of ThetaType should be considered uses of XType, even though 2 different typevars were setup? Why would adding the bound
optional argument automatically collapse them back together? The source does not enforce presence of bound, or any arguments beside name, in any way.
I don't think it’s typing/mypy’s job to infer your intentions in type declarations, only to check your code vs your declared type intentions. If you mean them to be the same then declare only 1 TypeVar. Considering them the same could lose some semantic meaning if you had actual reasons to have 2.
I’ll add to that bound
allows more flexibility than constraints
as it matches on subclasses. Let’s say you’ve user-defined 4 subclasses of int. Int1(int), Int2, Int3, Int4.... Now you’ve decided to partition your code where some of it should only accept Int1 and Int2. Typevarint12 could somewhat express that, even though your subclasses all match bound=int
.