from typing import TypeVar
A = TypeVar("A")
B = TypeVar("B")
def compare(a: A, b: B) -> bool:
return a < b
compare("first", "second")
One way to try to solve this is to make a comparable protocol. Now comparing a < b will type check, but now you cannot use builtin types like str even if str do have a __lt__ method:

from abc import abstractmethod
from typing import Protocol, TypeVar
C = TypeVar("C", bound="Comparable")
class Comparable(Protocol):
@abstractmethod
def __lt__(self: C, other: C) -> bool:
pass
AC = TypeVar("AC", bound=Comparable)
BC = TypeVar("BC", bound=Comparable)
def compare2(a: AC, b: BC) -> bool:
return a < b
compare2("first", "second")
This gives error:

I think, you should declare Comparable as a Generic protocol.
This code works for me
from abc import abstractmethod
from typing import Protocol, TypeVar
C = TypeVar("C")
class Comparable(Protocol[C]):
@abstractmethod
def __lt__(self, other: C) -> bool:
...
AC = TypeVar("AC", bound=Comparable)
BC = TypeVar("BC", bound=Comparable)
def compare2(a: AC, b: BC) -> bool:
return a < b
compare2("first", "second")
There are a few issues here.
First, when you use a TypeVar within a generic function, it should generally appear more than once. In your compare2 function, AC appears only once and BC appears only once. That means there's nothing for the TypeVar solver to "solve" when matching these type variables.
@askurihin is on the right path. You want to use a generic protocol here. However, you can't bind to an unspecialized generic class. (Pylance/Pyright should generate an error if you try to do so. It doesn't currently, but I've filed an issue on that.)
Here's how I'd approach this problem.
from abc import abstractmethod
from typing import Protocol, TypeVar
_T = TypeVar("_T")
class ComparableTo(Protocol[_T]):
@abstractmethod
def __lt__(self, other: _T) -> bool:
pass
def compare2(a: ComparableTo[_T], b: _T) -> bool:
return a < b
compare2("first", "second")
You'll note that Pyright generates an error on the line return a < b. I think that's a bug, and I'll look into it.
Thanks for the feedback. Using a generic protocol fixed my issue.
I've added support for binary operator magic methods that operate on constrained TypeVars. This eliminates the error in my proposed solution above. This fix will be in the next release.
Thanks for fixing. Look forwards to test this.
This is addressed in Pyright 1.1.93, which I just published. It will also be included in the next release of Pylance.
Most helpful comment
There are a few issues here.
First, when you use a TypeVar within a generic function, it should generally appear more than once. In your
compare2function,ACappears only once andBCappears only once. That means there's nothing for the TypeVar solver to "solve" when matching these type variables.@askurihin is on the right path. You want to use a generic protocol here. However, you can't bind to an unspecialized generic class. (Pylance/Pyright should generate an error if you try to do so. It doesn't currently, but I've filed an issue on that.)
Here's how I'd approach this problem.
You'll note that Pyright generates an error on the line
return a < b. I think that's a bug, and I'll look into it.