python / mypy

Optional static typing for Python
https://www.mypy-lang.org/
Other
18.24k stars 2.79k forks source link

Mypy doesn't understand structural subtyping for nested TypedDicts #17754

Closed sh-at-cs closed 2 weeks ago

sh-at-cs commented 2 weeks ago

Bug Report

When a structural subtyping relation between two "inner" TypedDicts should cause "outer" TypedDicts that contain them (and only them) to be subtypes of each other as well, Mypy fails to understand this and reports that the outer subtype is incompatible with the outer supertype.

To Reproduce

from typing import TypedDict

class InnerSuper(TypedDict):
  x: str

class InnerSub(TypedDict):
  x: str
  y: int

class OuterSuper(TypedDict):
  inner: InnerSuper

class OuterSub(TypedDict):
  inner: InnerSub

# Mypy understands that InnerSub is a structural subtype of InnerSuper...
inner_sub: InnerSub
inner_super: InnerSuper = inner_sub  # no type error

# ... but not that OuterSub is a structural subtype of OuterSuper:
outer_sub: OuterSub
outer_super: OuterSuper = outer_sub  # type error

Expected Behavior

No type errors are reported by Mypy because OuterSub is a structural subtype of OuterSuper.

Actual Behavior

Mypy reports an error:

example.py:22: error: Incompatible types in assignment (expression has type "OuterSub", variable has type "OuterSuper")  [assignment]
Found 1 error in 1 file (checked 1 source file)

Your Environment


Now that it has turned out this is expected behavior (see comment below), a few keywords to let other people find this issue more easily: covariance, variance, invariance, covariant

JelleZijlstra commented 2 weeks ago

This is correct because TypedDict is invariant in its value types. It would be legal to write outer_super["inner"] = {"x": 3}, making the value incompatible with InnerSub.

https://peps.python.org/pep-0705/ will address this.