Skip to content

Wrong type inference with generics #10593

Closed
@septatrix

Description

@septatrix

Bug Report

(A clear and concise description of what the bug is.)

To Reproduce

from typing import Any, Dict, List, TypedDict, TypeVar, Type, TYPE_CHECKING

class User(TypedDict):
    id: int
    name: str

users: List[Dict[str, Any]] = [{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}]

T = TypeVar("T")

def try_cast(cls: Type[T], data: Any) -> T:
    return cls(**data)  # type: ignore

for user in users:
    user_unannotated = try_cast(User, user)
    user_annotated: User = try_cast(User, user)  # Incompatible types in assignment (expression has type "User", variable has type "User")

    if TYPE_CHECKING:
        reveal_type(try_cast)  # def [T] (cls: Type[T`-1], data: Any) -> T`-1
        reveal_type(try_cast(User, {}))  # typed.User*
        reveal_type(user)  # builtins.dict*[builtins.str, Any]
        reveal_type(user_unannotated)  # typed.User*
        reveal_type(user_unannotated["id"])  # builtins.object*
        reveal_type(user_annotated)  # TypedDict('typed.User', {'id': builtins.int, 'name': builtins.str})
        reveal_type(user_annotated["id"])  # builtins.int

Run mypy one the above code. It will generate an error like Incompatible types in assignment (expression has type "User", variable has type "User")

Expected Behavior

Mypy should accept both variants of the try_cast call and the type of user_...["id"] should be int in both cases.

Actual Behavior

The first invocation of try_cast works but the id attribute has the wrong type.
The second invocation of try_cast has an error however the id attribute has the correct type.

Your Environment

  • Mypy version used: mypy 0.812
  • Mypy command-line flags: -
  • Mypy configuration options from mypy.ini (and other config files): -
  • Python version used: Python 3.8.6
  • Operating system and version: Ubuntu 20.10

Additional Details

Pyright shows the expected behaviour here:

  /tmp/septatrix-playground-2021-06-08T14-58-03-QGJ/typed.py:19:21 - info: Type of "try_cast" is "(cls: Type[T@try_cast], data: Any) -> T@try_cast"
  /tmp/septatrix-playground-2021-06-08T14-58-03-QGJ/typed.py:20:21 - info: Type of "try_cast(User, {  })" is "User"
  /tmp/septatrix-playground-2021-06-08T14-58-03-QGJ/typed.py:21:21 - info: Type of "user" is "Dict[str, Any]"
  /tmp/septatrix-playground-2021-06-08T14-58-03-QGJ/typed.py:22:21 - info: Type of "user_unannotated" is "User"
  /tmp/septatrix-playground-2021-06-08T14-58-03-QGJ/typed.py:23:21 - info: Type of "user_unannotated["id"]" is "int"
  /tmp/septatrix-playground-2021-06-08T14-58-03-QGJ/typed.py:24:21 - info: Type of "user_annotated" is "User"
  /tmp/septatrix-playground-2021-06-08T14-58-03-QGJ/typed.py:25:21 - info: Type of "user_annotated["id"]" is "int"

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions