Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inheriting from a generic TypedDict with a T | None key inexplicably narrows the that key type to T #16395

Closed
daniil-berg opened this issue Nov 2, 2023 · 1 comment · Fixed by #16398

Comments

@daniil-berg
Copy link

daniil-berg commented Nov 2, 2023

Bug Report

Inheriting from a generic TypedDict with a T | None key inexplicably narrows the that key type to T.

To Reproduce

from typing import Generic, TypeVar, TypedDict

T = TypeVar("T")


class Foo(TypedDict, Generic[T], total=False):
    a: str | None
    ...


class Bar(Foo[T], total=False):
    ...


class Baz(Foo[int], total=False):
    ...


a_value: str | None

foo: Foo[int]
bar: Bar[int]
baz: Baz

reveal_type(foo)  # TypedDict('tests.Foo', {'a'?: Union[builtins.str, None]})
reveal_type(bar)  # TypedDict('tests.Bar', {'a'?: builtins.str})
reveal_type(baz)  # TypedDict('tests.Baz', {'a'?: builtins.str})

foo["a"] = a_value  # passes
bar["a"] = a_value  # error: Value of "a" has incompatible type "str | None"; expected "str"  [typeddict-item]
baz["a"] = a_value  # error: Value of "a" has incompatible type "str | None"; expected "str"  [typeddict-item]

Expected Behavior

No errors. For all three variables foo, bar, baz the revealed type of the a-key of the TypedDict should be Union[builtins.str, None].

Actual Behavior

The type of the a-key of bar and baz is revealed to be builtins.str instead of Union[builtins.str, None].

Thus, the last two assignments cause the errors:

error: Value of "a" has incompatible type "int | None"; expected "int"  [typeddict-item]
error: Value of "a" has incompatible type "int | None"; expected "int"  [typeddict-item]

Notable obsevations

  • It does not matter, if any key in Foo is actually declared to be of type T.
  • Declaring for example a: T | None and adjusting the a_value type accordingly makes no difference.
  • As soon as you make Foo non-generic, i.e. remove Generic[T] from the bases (and adjust Bar and Baz declarations accordingly), the error disappears.
  • If you declare the a-key to be of any other union type e.g. str | int (and adjust the a_value type accordingly), the error seems to disappear. (Obviously I have only tested a few combinations.)

Your Environment

  • Mypy version used: 1.6.1
  • Mypy command-line flags: -
  • Mypy configuration options from mypy.ini (and other config files): strict = True
  • Python version used: 3.11.4 (also reproduced with 3.9.17)
@JelleZijlstra
Copy link
Member

Confirmed in mypy playground: https://mypy-play.net/?mypy=latest&python=3.10&gist=4d53c16a3b06bad9014022b24b490d08

Feels like something is going wrong with strict-optional handling.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants