TypeVar describing a class that must subclass more than one class

Viewed 1920

I would like to create a type annotation T that describes a type that must be a subclass of both class A and class B.

T = TypeVar('T', bound=A) only specifies that T must be a subclass of A.

T = TypeVar('T', A, B) only specifies that T must be a subclass of A or a subclass of B but not necessarily both.

I actually want something like T = TypeVar('T', bound=[A, B]) which would mean T must subclass both A and B. Is there a standard way of doing this?

1 Answers

What you are looking for is an intersection type. Strictly speaking, I do not believe Python's type annotations support this (at least not yet). However, you can get something similar with a Protocol:

from typing import Protocol, TypeVar

class A:
    def foo(self) -> int:
        return 42
class B:
    def bar(self) -> bool:
        return False

class C(A, B): pass
class D(A): pass
class E(B): pass

class ABProtocol(Protocol):
    def foo(self) -> int: ...
    def bar(self) -> bool: ...

T = TypeVar('T', bound=ABProtocol)

def frobnicate(obj: T) -> int:
    if obj.bar():
        return obj.foo()
    return 0

frobnicate(C())
frobnicate(D())
frobnicate(E())

Mypy complains:

test.py:26: error: Value of type variable "T" of "frobnicate" cannot be "D"
test.py:27: error: Value of type variable "T" of "frobnicate" cannot be "E"

Of course, this requires you to explicitly annotate all the methods yourself, unfortunately, something like class ABProtocol(A, B, Protocol): pass isn't allowed

Related