Skip to content

Conversation

@ilevkivskyi
Copy link
Member

Fixes #7036

This adds more "fine grained" logic for overlap between TypedDict and Mapping used by overload checks and --strict-equality. Basically the rules are:

  • A TypedDict with some required keys is overlapping with Mapping[str, <some type>] if and only if every key type is overlapping with <some type>.
  • A TypedDict with no required keys overlaps with Mapping[str, <some type>] if and only if at least one of key types overlaps with <some type>.
  • Empty dictionaries are as usual in gray area, so we follow the same logic as in other places: an empty dictionary can't cause an overlap (i.e. TypedDict with no required keys doesn't overlap with Mapping[str, <some type>]), but is itself (i.e. Mapping[<nothing>, <nothing>]) overlapping with a TypedDict with no required keys.

@ilevkivskyi ilevkivskyi requested a review from Michael0x2a July 6, 2019 08:28
@silviogutierrez
Copy link

I checked this out eagerly, as I thought it covers a big use case of @cs-cordero and mine's. But now that I read it closely, it seems related but not the actual solution. That is, this still won't work:

from typing import Mapping

from mypy_extensions import TypedDict


def mapping_test(thing: Mapping[str, str]) -> bool:
    return True


class Foo(TypedDict):
    foo: str


a = Foo(foo="a")
b: Foo = {'foo': 'a'}


mapping_test(a)  # Errors out still, with below.
mapping_test(b)  # Errors out still, with below.
# Argument 1 to "mapping_test" has incompatible type "Foo"; expected "Mapping[str, str]"

You mention "overlap" but in this case, it's referring strictly to equality and overloads, not general types matching. Is that right?

Is our use case a big stretch?

Either way, thanks for all the work on mypy!

@silviogutierrez
Copy link

Adding a more thorough snippet that demonstrates what works and what doesn't as of this PR, in case it's helpful. All running with --strict-equality

from typing import Mapping

from mypy_extensions import TypedDict


def mapping_test(thing: Mapping[str, str]) -> bool:
    return True


class Foo(TypedDict):
    foo: str


a = Foo(foo="a")
b: Foo = {"foo": "a"}
c: Mapping[str, str] = {"foo": "a"}


a == b == c  # Works


mapping_test(c)  # Works, of course
mapping_test(a)  # Does not work
mapping_test(b)  # Does not work

Copy link
Collaborator

@Michael0x2a Michael0x2a left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had one minor suggestion, but otherwise lgtm!

only if at least one of key types overlaps with <some type>. For example:

- TypedDict(x=str, y=str, total=False) overlaps with Dict[str, str]
- TypedDict(x=str, y=str, total=False) doesn't overlap with Dict[str, int]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a third example could be - TypedDict(x=int, y=str, total=False) overlaps with Dict[str, str]`?

@ilevkivskyi
Copy link
Member Author

@silviogutierrez Your issue is completely unrelated. The code is your example is unsafe because typed dicts use structural subtyping, you can use Mapping[str, object] instead.

@ilevkivskyi ilevkivskyi merged commit 8782d63 into python:master Jul 8, 2019
@ilevkivskyi ilevkivskyi deleted the typeddict-overlap branch July 8, 2019 15:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Dict and TypedDict are considered non-overlapping

3 participants