Skip to content

Unexpected error when using get() with union of typed dicts #7151

Closed

Description

@JukkaL

This generates an error which could be understood as a false positive:

from typing import Union
from mypy_extensions import TypedDict

class A(TypedDict):
    x: int

class B(TypedDict):
    y: str

d: Union[A, B]
d.get('x')  # TypedDict "B" has no key 'x'

It was introduced by #6560. Technically this is not a false positive, since a value with type A could have a hidden y attribute with a non-str type. However, maybe we should treat this as a false positive since the behavior probably feels unexpected for most users.

cc @ilevkivskyi

Activity

  1. ilevkivskyi commented on Jul 4, 2019

    @ilevkivskyi
    Member

    I don't see how it is specific to unions:

    d: B
    d.get('x')  # E: TypedDict "B" has no key 'x'
    
  2. JukkaL commented on Jul 4, 2019

    @JukkaL
    CollaboratorAuthor

    This specific to union in the sense that if one of the union items contains the key, it seems reasonable to use d.get('x') to have it not fail on other union items. That is, the question is if it would be better if unions would behave differently than non-unions. We'd only generate the error if no union items contain the key.

    Example:

    d: Union[A, B]
    d.get('x')  # Maybe this should be ok, since 'x' is defined in A?
    d.get('y')  # Ditto, since 'y' is defined in B
    d.get('bad')  # This should still be an error, since this could be a typo or totally bogus

    I'm not convinced that this should be changed, but I think it's worth at least thinking about it. The change would clearly be quite ad hoc.

  3. JukkaL commented on Jul 4, 2019

    @JukkaL
    CollaboratorAuthor

    I saw actually an instance of this in the Dropbox S codebase.

  4. Michael0x2a commented on Jul 4, 2019

    @Michael0x2a
    Collaborator

    Shouldn't d.get('bad') return None? Maybe that's the bug here -- when we do d.get('x') where d is a Union[A, B], we should be inferring Union[int, None] instead of an error.

    This would be a change in semantics, but it seems like a reasonable one to me? If users want the "this key doesn't exist" error, they could fall back to using indexing and do d['x']. Or maybe there's some nuance of TypedDicts that I'm missing.

  5. ilevkivskyi commented on Jul 4, 2019

    @ilevkivskyi
    Member

    I am fine with either behaviour of .get() itself, but I don't really like the idea that in some specific cases our unions should behave as unsafe unions (a.k.a. pytype unions).

  6. JukkaL commented on Jul 5, 2019

    @JukkaL
    CollaboratorAuthor

    There are a few reasons why d.get('bad') doesn't return None:

    • There could be a derived TypedDict that includes the key 'bad' with an arbitrary type. So technically a safe return type would be object.
    • When performing refactoring, it's useful that mypy complains about misspelled keys. For example, if you rename a key but it's used in .get(...) with a non-total TypedDict, mypy wouldn't complain about an invalid key and this could cause a bug.
    • If there's a typo in the key (such as writing foobar instead of fooBar), the .get(...) would silently work but produce incorrect output, and mypy wouldn't complain about it either.
  7. JukkaL commented on Jan 29, 2020

    @JukkaL
    CollaboratorAuthor

    This doesn't important enough to keep open, since no consensus was reached about if (and how) this should be fixed.

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

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions