mypy icon indicating copy to clipboard operation
mypy copied to clipboard

Allow unpacking of TypedDict into TypedDict

Open eflorico opened this issue 3 years ago • 3 comments

Description

Closes #9408 (and duplicates, e.g. #10585, #11108). Related tracking issue: #11753

Allows unpacking of TypedDicts into other TypedDicts:

class Foo(TypedDict):
    a: int

class Bar(TypedDict):
    a: int
    b: int

foo1: Foo = {'a': 1}
foo2: Foo = {**foo1}
bar: Bar = {**foo1, 'b': 2}

Should handle NotRequired fields correctly (they are counted as "maybe" present, we don't rely on them being present, but they should never be extraneous or have an incompatible type).

Also handles overwriting of fields correctly, for example:

class Foo(TypedDict):
    a: int
    b: str

class Bar(TypedDict):
    a: int
    b: int

foo: Foo = {'a': 1, 'b': '...'}
bar: Bar = {**foo} # Error: b is str instead of int
bar: Bar = {**foo, 'b': 2} # No error: b is overwritten

Limitations

  • Unpacking only works if the type of the unpacked expression is correctly inferred as a TypedDict. This is the case for variables or function return values, but not for dict expressions such as in foo: Foo = {**{'a': 1}}
  • Unpacking into unions does not work (foo_or_bar: Union[Foo, Bar] = {**foo1}). This is because find_typeddict_context can currently only handle one TypedDict in the context
  • Unpacking from unions does not work (foo: Bar = {**foo_or_bar}). I'm not sure yet how that could be added, I feel like it might take a bit more work

Test Plan

I have added quite detailed tests in check-typeddict.test.

eflorico avatar Aug 07 '22 19:08 eflorico

Diff from mypy_primer, showing the effect of this PR on open source code:

steam.py (https://github.com/Gobot1234/steam.py)
- steam/profile.py:217: error: Expected TypedDict key to be string literal  [misc]
+ steam/profile.py:217: error: Any cannot be unpacked into TypedDict (must be TypedDict)  [misc]
- steam/profile.py:302: error: Expected TypedDict key to be string literal  [misc]
+ steam/profile.py:302: error: Any cannot be unpacked into TypedDict (must be TypedDict)  [misc]

github-actions[bot] avatar Aug 07 '22 22:08 github-actions[bot]

Thanks, this is not a full review. Just a couple of minor comments 🙂

Thanks for your comments! I have updated the PR accordingly

eflorico avatar Aug 08 '22 20:08 eflorico

Diff from mypy_primer, showing the effect of this PR on open source code:

steam.py (https://github.com/Gobot1234/steam.py)
- steam/profile.py:217: error: Expected TypedDict key to be string literal  [misc]
+ steam/profile.py:217: error: Any cannot be unpacked into TypedDict (must be TypedDict)  [misc]
- steam/profile.py:302: error: Expected TypedDict key to be string literal  [misc]
+ steam/profile.py:302: error: Any cannot be unpacked into TypedDict (must be TypedDict)  [misc]

github-actions[bot] avatar Aug 08 '22 21:08 github-actions[bot]

Any progress on this PR ?

HansAarneLiblik avatar Oct 13 '22 09:10 HansAarneLiblik

@HansAarneLiblik I have recently tried to merge master, but my changes were clashing with these recent changes. I haven't had the chance to figure out yet how to best solve this. If you have time to merge the current master and figure out how to unite both changes, I'd appreciate your help! I will only get around to look into this MR again in about two weeks

eflorico avatar Oct 18 '22 22:10 eflorico

This was superseded by https://github.com/python/mypy/pull/15425

Thank you for your efforts anyway, the test cases were in particular helpful!

ilevkivskyi avatar Jun 26 '23 23:06 ilevkivskyi