Recursive type aliases tracker
- [ ] mypy (python/mypy#731), supported using a flag
- [x] pytype
- [x] pyright
- [x] pyre
- [x] PyCharm
Test case:
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(""), ""))
Recursive types should work with pytype. We don't support the X | Y syntax for union types yet, so the test case has to be modified a bit, but pytype accept this:
from typing import TypeAlias, Union
Recursive: TypeAlias = Union[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(""), "")))
(I also changed the list[...]s to list(..) in the last two lines; I assume that was a typo.)
FWIW mypy version in master supports this under a flag (expect it fails on the last line because of an unrelated bug in special-casing tuple literals, should be a 1-line fix).
In general, I guess we will discover a bunch of inconsistencies in type inference in mypy (currently it is a bit arbitrary in some situations, while it should be quite consistent for recursive types, especially if invariant collections are involved, e.g. this test passes if I use Sequence)
If I want to define nested list type should I use
NestedList = list[Union[str, "NestedList"]]
def f(l: NestedList) -> int:
…
or
Recursive: TypeAlias = list[str | list["Recursive"]]
def f(l: Recursive) -> int:
…
?
Seems like we're all done here! We have our first recursive types successfully added to typeshed: #9134
Thanks @ilevkivskyi for all the incredible work on mypy recently to enable this!
These are not working for me:
WorkerResult: TypeAlias = Result[str, str] | Result[object, str]
Worker: TypeAlias = Callable[[], WorkerResult | Worker]
gives:
Traceback (most recent call last):
File "/app/src/app/main.py", line 17, in <module>
Worker: TypeAlias = Callable[[], WorkerResult | Worker]
^^^^^^
NameError: name 'Worker' is not defined
Python version 3.11.2 (which should include whatever work happened in Nov 2022 on mypy).
This issue is about support in type checkers, not at runtime. To prevent the runtime NameError you'll (still) have to enclose the name in quotes.