Repository: python/mypy

Issue:
TypeAlias cannot be used as a metaclass
Here's a quick sample of a problem:

```python
from typing_extensions import TypeAlias

Explicit: TypeAlias = type
Implicit = type

class E(metaclass=Explicit): ...  # Invalid metaclass "Explicit"
class I(metaclass=Implicit): ...  # Invalid metaclass "Implicit"
```

I think that using type aliases should be allowed in metaclasses.

Why?
1. This blocks conditional metaclasses in `typeshed`, see https://github.com/python/typeshed/pull/8497

For example, see `dataclasses.pyi` definition: 

```python
if sys.version_info >= (3, 9):
    _InitVarMeta = type
else:
    class _InitVarMeta(type): ...  # whatever methods
```

2. It is valid in runtime
3. I don't think that unaliasing an alias here can lead to some type-checking problems (yes-yes, we have recursive type-aliases and what not 😒)



Discussion:

Resolving patch:
diff --git a/mypy/semanal.py b/mypy/semanal.py
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -1985,15 +1985,27 @@ def analyze_metaclass(self, defn: ClassDef) -> None:
             if isinstance(sym.node, PlaceholderNode):
                 self.defer(defn)
                 return
-            if not isinstance(sym.node, TypeInfo) or sym.node.tuple_type is not None:
+
+            # Support type aliases, like `_Meta: TypeAlias = type`
+            if (
+                isinstance(sym.node, TypeAlias)
+                and sym.node.no_args
+                and isinstance(sym.node.target, ProperType)
+                and isinstance(sym.node.target, Instance)
+            ):
+                metaclass_info: Optional[Node] = sym.node.target.type
+            else:
+                metaclass_info = sym.node
+
+            if not isinstance(metaclass_info, TypeInfo) or metaclass_info.tuple_type is not None:
                 self.fail(f'Invalid metaclass "{metaclass_name}"', defn.metaclass)
                 return
-            if not sym.node.is_metaclass():
+            if not metaclass_info.is_metaclass():
                 self.fail(
                     'Metaclasses not inheriting from "type" are not supported', defn.metaclass
                 )
                 return
-            inst = fill_typevars(sym.node)
+            inst = fill_typevars(metaclass_info)
             assert isinstance(inst, Instance)
             defn.info.declared_metaclass = inst
         defn.info.metaclass_type = defn.info.calculate_metaclass_type()


Test patch:
diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test
--- a/test-data/unit/check-classes.test
+++ b/test-data/unit/check-classes.test
@@ -4486,6 +4486,57 @@ class A(metaclass=M):
 reveal_type(A.y) # N: Revealed type is "builtins.int"
 A.x # E: "Type[A]" has no attribute "x"
 
+[case testValidTypeAliasAsMetaclass]
+from typing_extensions import TypeAlias
+
+Explicit: TypeAlias = type
+Implicit = type
+
+class E(metaclass=Explicit): ...
+class I(metaclass=Implicit): ...
+[builtins fixtures/classmethod.pyi]
+
+[case testValidTypeAliasOfTypeAliasAsMetaclass]
+from typing_extensions import TypeAlias
+
+Explicit: TypeAlias = type
+Implicit = type
+
+A1: TypeAlias = Explicit
+A2 = Explicit
+A3: TypeAlias = Implicit
+A4 = Implicit
+
+class C1(metaclass=A1): ...
+class C2(metaclass=A2): ...
+class C3(metaclass=A3): ...
+class C4(metaclass=A4): ...
+[builtins fixtures/classmethod.pyi]
+
+[case testTypeAliasWithArgsAsMetaclass]
+from typing import Generic, TypeVar
+from typing_extensions import TypeAlias
+
+T = TypeVar('T')
+class Meta(Generic[T]): ...
+
+Explicit: TypeAlias = Meta[T]
+Implicit = Meta[T]
+
+class E(metaclass=Explicit): ...  # E: Invalid metaclass "Explicit"
+class I(metaclass=Implicit): ...  # E: Invalid metaclass "Implicit"
+[builtins fixtures/classmethod.pyi]
+
+[case testTypeAliasNonTypeAsMetaclass]
+from typing_extensions import TypeAlias
+
+Explicit: TypeAlias = int
+Implicit = int
+
+class E(metaclass=Explicit): ...  # E: Metaclasses not inheriting from "type" are not supported
+class I(metaclass=Implicit): ...  # E: Metaclasses not inheriting from "type" are not supported
+[builtins fixtures/classmethod.pyi]
+
 [case testInvalidVariableAsMetaclass]
 from typing import Any
 M = 0  # type: int