Repository: python/mypy

Issue:
Help genericising a class
I have the following class:

```py
class DictWrapper:

    def __init__(self, dict_type: Type[MutableMapping]) -> None: ...

    def get_internal_thing(self) -> MutableMapping[str, str]: ...
```

which seems to work fine. However, if I want to be able to treat the return value of `get_internal_thing` as the type that was passed in to `__init__` (e.g. `DictWrapper(OrderedDict).get_internal_thing().move_to_end('a')`) then I (unsurprisingly) run in to problems.

I've tried genericising it like this:

```py
T = TypeVar('T')


class GenericDictWrapper(Generic[T]):

    def __init__(self, dict_type: Type[T]) -> None: ...

    def get_internal_thing(self) -> T: ...
```

As I've dropped all reference to str, it's not especially surprising that it doesn't work;

```py
GenericDictWrapper(OrderedDict).get_internal_thing().move_to_end('a')
```

gives:

```
test.pyi:22: error: Argument 1 to "move_to_end" of "OrderedDict" has incompatible type "str"; expected "_KT"
```

I'm not really sure where to proceed from here; it really feels like I'm missing something. Can anyone help me out?


Discussion:
Another thing that occurred to me as a possibility was bounding my type variable:

```py
S = TypeVar('S', bound=MutableMapping[str, str])


class BoundGenericDictWrapper(Generic[S]):

    def __init__(self, dict_type: Type[S]) -> None: ...

    def get_internal_thing(self) -> S: ...
```

but this still doesn't work:

```py
BoundGenericDictWrapper(OrderedDict).get_internal_thing().move_to_end('a')
```

gives:

```
test.pyi:35: error: Value of type variable "S" of "BoundGenericDictWrapper" cannot be OrderedDict[_KT, _VT]
test.pyi:35: error: Argument 1 to "move_to_end" of "OrderedDict" has incompatible type "str"; expected "_KT"
```
I have just noticed that passing an `OrderedDict[str, str]` in does cause both `GenericDictWrapper` and `BoundGenericDictWrapper` to work as I expect, but existing callers of the code just specify what dict implementation they want to use without typing information, and I'd like to retain that.
I think this is a bug in how ``Type[t]`` is implemented. It should never complain about ``_KT`` because these type variables should be substituted with ``Any``.
I've spent a while digging in to this, and the following diff solves my case and doesn't break any existing tests.

```diff
diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
index 4ac17c37..5658b7b0 100644
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -154,6 +154,10 @@ class ExpressionChecker(ExpressionVisitor[Type]):
         elif isinstance(node, TypeInfo):
             # Reference to a type object.
             result = type_object_type(node, self.named_type)
+            if isinstance(self.type_context[-1], TypeType):
+                # This is the type in a Type[] expression, so substitute type
+                # variables with Any.
+                result = erasetype.erase_typevars(result)
         elif isinstance(node, MypyFile):
             # Reference to a module object.
             try:
```
Actually, this seems to be the right idea. Would you like to make a PR (with tests)?
For sure!

Resolving patch:
diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -154,6 +154,10 @@ def analyze_ref_expr(self, e: RefExpr, lvalue: bool = False) -> Type:
         elif isinstance(node, TypeInfo):
             # Reference to a type object.
             result = type_object_type(node, self.named_type)
+            if isinstance(self.type_context[-1], TypeType):
+                # This is the type in a Type[] expression, so substitute type
+                # variables with Any.
+                result = erasetype.erase_typevars(result)
         elif isinstance(node, MypyFile):
             # Reference to a module object.
             try:


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
@@ -3373,6 +3373,17 @@ reveal_type(f(e1t))  # E: Revealed type is '__main__.A'
 
 reveal_type(f(''))  # E: Revealed type is 'builtins.str'
 
+[case testTypeCErasesGenericsFromC]
+from typing import Generic, Type, TypeVar
+
+K = TypeVar('K')
+V = TypeVar('V')
+class ExampleDict(Generic[K, V]): ...
+
+D = TypeVar('D')
+def mkdict(dict_type: Type[D]) -> D: ...
+reveal_type(mkdict(ExampleDict))  # E: Revealed type is '__main__.ExampleDict*[Any, Any]'
+
 -- Synthetic types crashes
 -- -----------------------