pythonpython-typingmypy

How to add Python type annotations to a class that inherits from itself?


I'm trying add type annotations to an ElementList object that inherits from list and can contain either Element objects or other ElementGroup objects.

When I run the following code through mypy:

from typing import Self


class Element:
    pass


class ElementList(list[Element | Self]):
    pass


elements = ElementList(
    [
        Element(),
        Element(),
        ElementList(
            [
                Element(),
                Element(),
            ]
        ),
    ]
)

I get the following error:

element.py:8: error: Self type is only allowed in annotations within class definition  [misc]
Found 1 error in 1 file (checked 1 source file)

What's the recommended way to add typing annotations to this so that mypy doesn't throw an error?


Solution

  • Your sample list argument to the ElementList constructor contains not just Elements and ElementLists but also actual lists, so a workaround of class ElementList(list["Element | ElementList"]): ... would not have worked, as @dROOOze pointed out in the comment, because list is not a subtype of ElementList.

    You can work around this limitation with a type alias, which can refer to itself without creating a subtype:

    class Element:
        pass
    
    type ElementListType[T] = Element | T | list[ElementListType[T]]
    
    class ElementList(list[ElementListType["ElementList"]]):
        pass
    
    elements = ElementList(
        [
            Element(),
            Element(),
            [
                Element(),
                ElementList(
                    [
                        Element(),
                        Element(),
                    ]
                )
            ],
        ]
    )
    

    Demo with mypy here

    Demo with pyright here