7

Consider:

from __future__ import annotations

class A:
    @classmethod
    def get(cls) -> A:
        return cls()

class B(A):
    pass

def func() -> B: # Line 12
    return B.get()

Running mypy on this we get:

$ mypy test.py
test.py:12: error: Incompatible return value type (got "A", expected "B")
Found 1 error in 1 file (checked 1 source file)

Additionally, I have checked to see if old-style recursive annotations work. That is:

# from __future__ import annotations

class A:
    @classmethod
    def get(cls) -> "A":
# ...

...to no avail.

Of course one could do:

from typing import cast

def func() -> B: # Line 12
    return cast(B, B.get())

Every time this case pops up. But I would like to avoid doing that.

How should one go about typing this?

2
  • I'm unsure about what you're asking. If you want to return A, you need to cast it explicitely. If you just want to type-annotate your function correctly, why not def func() -> A? Commented Jan 29, 2021 at 23:53
  • I actually want to return B in func. For example, suppose we have classes representing database entities. We have a common class Entity and subclasses like User and Project. These subclasses also have additional, idiosyncratic attributes that matter. We want to be explicit about whether your returning a User or Project because that will constrain what we can do downstream. Commented Jan 29, 2021 at 23:58

1 Answer 1

5

The cls and self parameters are usually inferred by mpyp to avoid a lot of redundant code, but when required they can be specified explicitly by annotations.

In this case the explicit type for the class method would look like the following:

class A:
    @classmethod
    def get(cls: Type[A]) -> A:
        return cls()

So what we really need here is a way to make Type[A] a generic parameter, such that when the class method is called from a child class, you can reference the child class instead. Luckily, we have TypeVar values for this.

Working this into your existing example we will get the following:

from __future__ import annotations

from typing import TypeVar, Type


T = TypeVar('T')


class A:
    @classmethod
    def get(cls: Type[T]) -> T:
        return cls()


class B(A):
    pass


def func() -> B:
    return B.get()

Now mypy should be your friend again! 😎

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.