diff --git a/mypy/types.py b/mypy/types.py index e0e897e04cad..2f1f9570fda9 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -3941,6 +3941,14 @@ def visit_type_alias_type(self, t: TypeAliasType, /) -> list[mypy.nodes.TypeAlia return [t.alias] + t.alias.target.accept(self) return [] + def visit_instance(self, t: Instance) -> list[mypy.nodes.TypeAlias]: + aliases = super().visit_instance(t) + special_alias = t.type.special_alias + if special_alias is None or special_alias in self.seen_alias_nodes: + return aliases + self.seen_alias_nodes.add(special_alias) + return aliases + [special_alias] + special_alias.target.accept(self) + def is_named_instance(t: Type, fullnames: str | tuple[str, ...]) -> TypeGuard[Instance]: if not isinstance(fullnames, tuple): diff --git a/test-data/unit/check-namedtuple.test b/test-data/unit/check-namedtuple.test index 45de2a9e50ae..9039ef3820e5 100644 --- a/test-data/unit/check-namedtuple.test +++ b/test-data/unit/check-namedtuple.test @@ -1530,3 +1530,20 @@ class Base: names = [name for name in namespace if fail] # E: Name "fail" is not defined self.n = namedtuple("n", names) # E: NamedTuple type as an attribute is not supported [builtins fixtures/tuple.pyi] + +[case testNamedTupleRecursiveTypeAlias] +from __future__ import annotations +from typing import Awaitable, NamedTuple, TypeVar, Union + +T = TypeVar("T") +AwaitOrValue = Union[Awaitable[T], T] + +class Leaf(NamedTuple): + data: Wrapper + +class Wrapper(NamedTuple): + result: AwaitOrValue[Leaf] + +def foo(value: Leaf | Wrapper) -> None: + wrapper: Wrapper = value # E: Incompatible types in assignment (expression has type "Union[Leaf, Wrapper]", variable has type "Wrapper") +[builtins fixtures/tuple.pyi]