Finding "implicit" type aliases

We recently ported our project up to a minimum Python version of 3.12. As such we are able to use the new type soft keyword that seems to be the preferred way to write (most) type aliases now.

Ruff is able to detect and translate explicit uses of TypeAlias

StrDict: TypeAlias = dict[str, Any]

But is there anyway (with Ruff, Mypy, etc) to detect “implicit/bare” type aliases that aren’t explicitly annotated?

StrDict = dict[str, Any]

We’d like to upgrade all of our implicit type aliases to the new, explicit/recommended syntax but trying to find these manually in a large codebase is challenging.

2 Likes

Mypy can do this for you with a little patching. This diff would do the trick:

diff --git a/mypy/semanal.py b/mypy/semanal.py
index d70abe911..22060c2a3 100644
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -4125,6 +4125,8 @@ class SemanticAnalyzer(
                 self.fail("Type aliases are prohibited in protocol bodies", s)
                 if not lvalue.name[0].isupper():
                     self.note("Use variable annotation syntax to define protocol members", s)
+        if not pep_613 and not pep_695:
+            self.fail(f'Found implicit alias "{lvalue.name}"', lvalue)
         return True
 
     def check_type_alias_type_call(self, rvalue: Expression, *, name: str) -> TypeGuard[CallExpr]:

(a quick way to apply this patch would be to cd to the top of your mypy clone, run git apply -, paste the diff into the terminal, then hit CTRL-D)

Then given:

from typing import Any, TypeAlias

StrDict1 = dict[str, Any]
StrDict2: TypeAlias = dict[str, Any]
type StrDict3 = dict[str, Any]

mypy will ouput:

implicit.py:3: Found implicit alias "StrDict1"  [misc]
    StrDict1 = dict[str, Any]
    ^~~~~~~~
Found 1 error in 1 file (checked 1 source file)
5 Likes

That looks helpful! Although I’d prefer a specific error code instead of [misc], which helps in case this was intentional (e.g. on python <3.10).

1 Like