You can specify recursive types in the typing language by using type aliases and forward reference strings,

Garthoks = Union[Garthok, Iterable['Garthoks']]

Mypy supports recursive types by default since v0.990, and Pyright/Pylance since v2020.9.4.

Some types of forward references are handled by PEP 563. You can use them starting from Python 3.7 by doing from __future__ import annotations – Konstantin


As of Python 3.12, __future__.annotations/stringifying is not necessary if the type is defined using a type statement:

type Garthoks = Garthok | Iterable[Garthoks]
Answer from gilch on Stack Overflow
🌐
Python.org
discuss.python.org › typing
Recursive type annotation for a nested list of lists of lists of - Typing - Discussions on Python.org
December 11, 2024 - I’m trying to figure out the way to write type annotations to represent a type nested[T,N] such that: nested[T,0] = list[T] nested[T,1] = list[list[T]] nested[T,2] = list[list[list[T]]] etc Is there a good way of doing this? I have hundreds of functions with signatures mostly being like: def func1(p: nested[T,0]) -> T: ... def func2(p: nested[T,0], q: nested[T,0]) -> nested[T,0]: ... def func3(p: nested[T,N], q: nested[T,N], N: int) -> nested[T,N]: ... def func4(p: nested[T,N], ...
Discussions

Generic `typing.ForwardRef` to support generic recursive types - Ideas - Discussions on Python.org
Since v0.981 mypy supports recursive types and will be enabled by default since v0.990. E.g.: JSON = Union[Dict[str, 'JSON'], List['JSON'], str, int, float, bool, None] Recursive types need to use ForwardRefs at the right-hand side to reference the type alias before assignment. More on discuss.python.org
🌐 discuss.python.org
2
October 21, 2022
Recursive Generic Type Support
Bug Report In: #731 #13297 (not released yet in v0.971) We now support recursive type hints, such as: JSON = Union[Dict[str, 'JSON'], List['JSON'], str, int, float, bool, None] But ... More on github.com
🌐 github.com
1
September 20, 2022
Recursive type aliases tracker
mypy (python/mypy#731), supported using a flag pytype pyright pyre PyCharm Test case: from typing import TypeAlias Recursive: TypeAlias = str | list["Recursive"] def foo(r: Recursive) -&g... More on github.com
🌐 github.com
8
May 21, 2022
python - Recursive type checking for containers - Code Review Stack Exchange
This code checks the type, length and depth (highest number of nested containers) of the container as well as the type of its sub-containers and its elements. There are also two simple methods for ... More on codereview.stackexchange.com
🌐 codereview.stackexchange.com
March 20, 2021
🌐
Reddit
reddit.com › r/learnpython › recursive type hints
r/learnpython on Reddit: Recursive type hints
February 25, 2024 -

I have lists within lists with integer items (or bytes, but for example purposes ints are enough), for example: [1, 2, [3, 4], 5]

How can I go about creating a type hint for this structure?

This is what I've made:

from typing import List, Union

NestedList = List[Union[int, 'NestedList']]

test: NestedList = [1, 2, [3], 4]
test.append([5])
test = test[-1]

But mypy fails with:

test_recursive_hints.py:7: error: Incompatible types in 
assignment (expression has type "int | NestedList", 
variable has type "NestedList")  [assignment]

How to solve this problem?

🌐
Turingtaco
turingtaco.com › recursive-types
Recursive Types in Python - The Turing Taco Tales
December 7, 2024 - This circular dependency creates a mutually recursive relationship, posing a challenge for Python's type system. To resolve this, we use forward references. Here's how the classes might be defined: ... from typing import Union, List class Value: def __init__(self, data: int): self.data = data def evaluate(self) -> int: return self.data class Operation: def __init__(self, operands: List['Expression']): self.operands = operands def evaluate(self) -> int: # Placeholder for operation logic (e.g., addition, multiplication) # For simplicity, let's assume it's a sum of operands return sum(operand.evaluate() for operand in self.operands) class Expression: def __init__(self, content: Union[Value, Operation]): self.content = content def evaluate(self) -> int: return self.content.evaluate()
🌐
Python.org
discuss.python.org › ideas
Generic `typing.ForwardRef` to support generic recursive types - Ideas - Discussions on Python.org
October 21, 2022 - Since v0.981 mypy supports recursive types and will be enabled by default since v0.990. E.g.: JSON = Union[Dict[str, 'JSON'], List['JSON'], str, int, float, bool, None] Recursive types need to use ForwardRefs at the right-hand side to reference the type alias before assignment.
🌐
Gitbooks
wizardforcel.gitbooks.io › sicp-in-python › content › 18.html
3.3 Recursive Data Structures | SICP in Python - wizardforcel
Recall that the recursive list abstract data type represented a list as a first element and the rest of the list. We previously implemented recursive lists using functions, but at this point we can re-implement them using a class.
🌐
University of Toronto
cs.toronto.edu › ~david › course-notes › csc110-111 › 14-induction-and-recursion › 05-recursive-lists.html
14.5 Recursive Lists
So instead, we will define a new recursive Python class to represent this recursive definition. Here is our first attempt: from __future__ import annotations from typing import Any class RecursiveList: """A recursive implementation of the List ADT. """ # Private Instance Attributes: # - _first: The first item in this list. # - _rest: A list containing the items in this list that come after the first one. _first: Any _rest: RecursiveList · We say that this RecursiveList data type is recursive because its _rest instance attribute refers to another instance of RecursiveList.
Find elsewhere
🌐
GitHub
github.com › python › mypy › issues › 13693
Recursive Generic Type Support · Issue #13693 · python/mypy
September 20, 2022 - Bug Report In: #731 #13297 (not released yet in v0.971) We now support recursive type hints, such as: JSON = Union[Dict[str, 'JSON'], List['JSON'], str, int, float, bool, None] But ...
Author: python
🌐
GitHub
github.com › python › typeshed › issues › 7904
Recursive type aliases tracker · Issue #7904 · python/typeshed
May 21, 2022 - from typing import TypeAlias Recursive: TypeAlias = str | list["Recursive"] def foo(r: Recursive) -> None: if not isinstance(r, str): foo(r[0]) if not isinstance(r, list): r.casefold() foo("") foo(list("")) foo(list(list(""), ""))
Author: python
Top answer
1 of 1
1

Assuming that you decide to retain _check_parameters(), here's an illustration of some alternative techniques for organizing a complex boolean check. (1) The isinstance() function will take a tuple of types, so you can do some consolidation. (2) If you need to check for None and check for types, you can do it all in one shot. (3) Some of your checks were repetitive; help the reader by factoring things out. (4) Organize the checks like a pretty-printed data structure because it gets the parens/brackets working for you to convey logical structure. (5) Sometimes simple and fairly banal code comments can function as visual/organizational sign posts to guide the reader. (6) I prefer the lines of code to lead with the substance rather than the boolean operator -- which is mostly a stylistic preference, but I do think it combines better in these kinds of complex checks. For example, I felt no readability-driven urge to add a preliminary True and to the expression. Also, most people use editors with syntax highlighting, so the operators pop out visually and there's no need to waste the prime real estate (the start of each line) on the operator.

TNone = type(None)

check_ctype_seq = lambda ctypes, cls: (
    isinstance(ctypes, cls) and
    all(
        isclass(ct) and
        issubclass(ct, GenericContainer)
        for ct in ctypes
    )
)

return (
    # Length and depth.
    isinstance(length, (int, Callable, TNone)) and
    isinstance(depth, (int, Callable, TNone)) and
    # Matching type.
    (
        isinstance(matching_type, (TNone, type)) or
        is_n_list(matching_type, None, type) or
        is_n_tuple(matching_type, None, type)
    ) and
    # Container_type.
    (
        container_type is None or
        (
            isclass(container_type)
            and issubclass(container_type, GenericContainer)
        ) or
        check_ctype_seq(container_type, list) or
        check_ctype_seq(container_type, tuple)
    )
)
🌐
Composingprograms
composingprograms.com › versions › v1 › pages › 27-recursive-data-structures.html
2.7 Recursive Data Structures
We can ensure this behavior by setting the rlist_expression function as the value of a special class attribute __repr__. Python displays instances of user-defined classes by invoking their __repr__ method. >>> Rlist.__repr__ = rlist_expression >>> s Rlist(3, Rlist(4, Rlist(5))) Using the Rlist class, we can define some common operations on recursive lists.
🌐
Open Book Project
openbookproject.net › thinkcs › python › english2e › ch11.html
11. Recursion and exceptions — How to Think Like a Computer Scientist: Learning with Python 2nd Edition documentation
The values of my_string, my_list, and my_tuple are not changed. If we want to use insert_in_middle to change them, we have to assign the value returned by the function call back to the variable: >>> my_string = insert_in_middle('c', my_string) >>> my_string 'abcde' All of the Python data types we have seen can be grouped inside lists and tuples in a variety of ways.
🌐
Reddit
reddit.com › r/python › recursive generic type hints (python 3.12)
r/Python on Reddit: Recursive Generic Type Hints (python 3.12)
April 10, 2025 -

TIL from this video typing a recursive flatten (by YT channel anthonywritescode) that you can now type hint recursive data & functions with generic type parameter!

# new syntax
# recursive type for nested list having elems of same type (eg. int)
type _RList[U] = list[U | _RList[U]]

def flatten[T](lst: _RList[T]) -> _RList[T]:
     """ Flatten nested list.""""
     return [
         flatten(x) if isinstance(x, list) else x
         for x in lst
     ]

NOTE: Latest mypy type checks this new syntax, but editor / IDE may not recognize it yet.

Did you all know about this? Have you found more such cool type hinting syntax in Python?

🌐
GitHub
github.com › python › mypy › issues › 731
Support recursive types · Issue #731 · python/mypy
July 30, 2015 - The following in particular would be useful: Callback = Callable[[str], 'Callback'] Foo = Union[str, List['Foo']]
Author: python
🌐
CSDN
devpress.csdn.net › python › 62fe0d8fc677032930804626.html
How can I make a recursive Python type defined over several aliases?_python_Mangs-Python
August 18, 2022 - Python How can I make a recursive Python type defined over several aliases? Answer a question I want this logical type structure: from typing import List, Dict, Union ObjectType = Dict[str, 'EntryType'] ListType = List['EntryType'] EntryType = Union[str, 'ListType', 'ObjectTy ·
🌐
Python documentation
docs.python.org › 3 › library › typing.html
typing — Support for type hints
1 week ago - >>> type ListOrSet[T] = list[T] | set[T] >>> ListOrSet.__type_params__ (T,) >>> type NotGeneric = int >>> NotGeneric.__type_params__ () ... The type alias’s value. This is lazily evaluated, so names used in the definition of the alias are not resolved until the __value__ attribute is accessed: >>> type Mutually = Recursive >>> type Recursive = Mutually >>> Mutually Mutually >>> Recursive Recursive >>> Mutually.__value__ Recursive >>> Recursive.__value__ Mutually