You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
We need to change the way we represent type aliases to be more like classes, to handle recursive aliases. Right the aliased type is stored inline, making recursion impossible; all recursive references are treated as Unknown.
This can be solved with a layer of indirection - we could have TypeAlias store a unique identifier, and look up the aliased type somewhere else. This would be similar to how we have a ClassType and use it to look up the ClassMetadata. Because of this indirection, we would need to store type arguments for any instantiated type aliases so that we can use it to instantiate the forall after looking up the type - the overall structure is very similar to Class v.s. ClassType.
This indirection would solve the problem for type aliases created with TypeAlias or the type statement.
There are two tricky parts here, related to how we extend the solution to implicit type aliases. Right now, we decide whether something is an implicit type alias at the solver phase, if the RHS expression is a particular type.
This means that we're missing information that we need in the bindings step:
whether or not to generate the new type alias bindings
whether or not we should process a string as a type or a string literal (we only call parse_type_literal on the ensure_type code path, so it only gets used in places where we're sure we're dealing with a type annotation - this means that stringified types don't work at all for implicit type aliases ATM, but they need to)
We could solve it by always generating the bindings, and having it resolve to either a type alias or some NotATypeAlias, and raise an error when we try to use the latter as a type alias. This feels pretty clunky though, and would be akin to having a bunch of Classes floating around where we need to verify that it's actually a class before we try to use it.
This issue has someone assigned, but has not had recent activity for more than 2 weeks.
If you are still working on this issue, please add a comment so everyone knows. Otherwise, please unassign yourself and allow someone else to take over.
One (possibly unreliable) heuristic for detecting implicit type aliases: | isn't supported between strings normally, so if we have an assignment whose RHS is a | node where at least one operand is a string literal & all the other operands are string literals or valid annotation syntax, we could try to handle it like a type alias.
We'd need to make sure we don't panic if this fails.
This issue has someone assigned, but has not had recent activity for more than 2 weeks.
If you are still working on this issue, please add a comment so everyone knows. Otherwise, please unassign yourself and allow someone else to take over.
type R=list[tuple[int, R]]
# ERROR sandbox.py:1:10-29: Found cyclic self-reference in `R` [[invalid-type-alias](https://pyrefly.org/en/docs/error-kinds/#invalid-type-alias)]`
Ah, that's caused by Pyrefly's check for cyclic self-references misfiring (#3235). It's supposed to catch obviously silly things like type R = list[R], but it's too aggressive. If the inner R is unioned with something else (for example, type R = list[tuple[int, int | R]]), then pyrefly will accept it.
Activity
[-]Recursive type aliases[/-][+]Feature: Recursive type aliases[/+]github-actions commentedon Jul 8, 2025
This issue has someone assigned, but has not had recent activity for more than 2 weeks.
If you are still working on this issue, please add a comment so everyone knows. Otherwise, please unassign yourself and allow someone else to take over.
Thank you for your contributions!
yangdanny97 commentedon Jul 24, 2025
One (possibly unreliable) heuristic for detecting implicit type aliases:
|isn't supported between strings normally, so if we have an assignment whose RHS is a|node where at least one operand is a string literal & all the other operands are string literals or valid annotation syntax, we could try to handle it like a type alias.We'd need to make sure we don't panic if this fails.
github-actions commentedon Aug 12, 2025
This issue has someone assigned, but has not had recent activity for more than 2 weeks.
If you are still working on this issue, please add a comment so everyone knows. Otherwise, please unassign yourself and allow someone else to take over.
Thank you for your contributions!
36 remaining items
Add targs to TypeAliasRef
Add display for TypeAliasRef with args
Store TParams in AliasRecursive
Store TParams and TArgs for generic recursive aliases
Do targs substitution in AnswersSolver::get_type_alias
Make `add_implicit_any_error` a little more general
Report implicit-any for generic type aliases
Support forward references in `TypeAliasType(value=...)`
Give a name to `TypeAliasParams::TypeAliasType`'s field
Create a helper function for legacy tparam computation
Make tvars_to_tparams_for_type_alias_type return tparams
Use a legacy tparam builder for TypeAliasType
JelleZijlstra commentedon May 31, 2026
Pyrefly doesn't seem to support recursive aliases using the type statement:
Other type checkers allow this.
rchen152 commentedon Jun 6, 2026
Ah, that's caused by Pyrefly's check for cyclic self-references misfiring (#3235). It's supposed to catch obviously silly things like
type R = list[R], but it's too aggressive. If the innerRis unioned with something else (for example,type R = list[tuple[int, int | R]]), then pyrefly will accept it.