As is often the case, I agree with Eric and find myself with little to add beyond a +1 to the position Eric suggested :-)

mypy's current heuristics are similar to pyright's, except that simple assignments in nested scope are considered variables, not aliases (as Nipunn mentioned, relevant code). With PEP 613, I personally don't feel particularly strongly about that additional heuristic... Although maybe there is an appeal to be made to Chesterton's Fence, given that Ivan and Jukka went out of their way to include that heuristic, even though the pre-PEP 613 world left users no recourse to get mypy to treat those as type aliases. I'll check how much code gets flagged by mypy_primer if I remove it.

Both of you are right that consistency is better for users. I did a bad job of expressing the core of the point I wanted to make, which was something like: if the intent of the user is clear (as the case of `def f(): X: TypeAlias = int` feels to me), I tend to default to the position that type checkers should follow that intent and it shouldn't be prohibited without a discussion like the one we're having now :-)

If we find we're in agreement / this thread dies off, I think I have the following action items:
1) Make PRs to clarify PEP 484 and loosen requirements in PEP 613
2) See if changing mypy's heuristic regarding nested scopes to match pyright causes regressions for mypy users
3) Update the mypy documentation page Nipunn linked

On Wed, 3 Nov 2021 at 11:56, Eric Traut <> wrote:
FWIW, pyright currently uses the following heuristics to distinguish an implicit (inferred) type alias definitions from a regular variable assignment.

A type alias is inferred if all of the following conditions apply:
1. There is no type annotation provided
2. There is only one assignment to the symbol
3. The expression on the RHS of the assignment does not contain any syntactic form that would be considered illegal for a type annotation (call expressions, lambdas, comprehensions, etc.)
4. The type evaluation of the RHS evaluates to an instantiable type or a union of instantiable types

The current heuristics don't require that the symbol is in the global (module-level) scope.

Points #1 and #2 generally distinguish class variables from class-scoped type aliases in the vast majority of cases. Even if these heuristics fail and incorrectly identify a class variable as a type alias, it tends to work fine (no false positive errors). I don't recall ever receiving a bug report or a question about this, so it seems to work well.

I do agree that it would be beneficial to standardize this behavior across type checkers.

I know that some pyright users rely on our current behavior, so there could be some pain if we were to change it, but (depending on what we conclude here) there's likely a straightforward workaround we can offer the affected users.


Eric Traut
Contributor to Pyright & Pylance
Typing-sig mailing list --
To unsubscribe send an email to
Member address: