Skip to content

TypedDict keys reuse? #4128

Open
Open

Description

@jstasiak

I may be in a need of the following - two TypeDicts with the same keys, one of them created with total=False:

from mypy_extensions import TypedDict

Details = TypedDict('Details', {'name': str, 'age': int, 'address': str})
DetailsSubset = TypedDict('DetailsSubset', {'name': str, 'age': int, 'address': str}, total=False)

Is there a way to reuse the keys instead of having to repeat the {'name': str, 'age': int, address: str} fragment twice?

I can't do

from mypy_extensions import TypedDict

keys = {'name': str, 'age': int, 'address': str}
Details = TypedDict('Details', keys)
DetailsSubset = TypedDict('DetailsSubset', keys, total=False)

Because

% mypy code.py
code.py:4: error: TypedDict() expects a dictionary literal as the second argument
code.py:5: error: TypedDict() expects a dictionary literal as the second argument

(which is quite reasonable, tried that on the off chance it'd work)

Activity

  1. ilevkivskyi commented on Oct 17, 2017

    @ilevkivskyi
    Member

    This is currently not possible. We will probably need the Const type modifier for this to work, see #1214

  2. JukkaL commented on Nov 16, 2018

    @JukkaL
    Collaborator

    We now have a Final modified but that isn't enough, since we never implicitly infer TypedDict types from dict expressions.

  3. ilevkivskyi commented on Dec 20, 2019

    @ilevkivskyi
    Member

    Another4 request for this appeared, see #8186, so raising priority to normal.

  4. ilevkivskyi commented on Dec 20, 2019

    @ilevkivskyi
    Member

    (Just to clarify, we can start from only supporting final names for individual keys, which should be pretty easy.)

  5. cwhy commented on Mar 12, 2020

    @cwhy

    @ilevkivskyi I agree that we can just support final names for keys first, at least for NamedTuple, as stated at the end of PEP-591:

    Type checkers should treat uses of a final name that was initialized with a literal as if it was replaced by the literal. For example, the following should be allowed:

    
    X: Final = "x"
    Y: Final = "y"
    N = NamedTuple("N", [(X, int), (Y, int)])
    

    It is sometimes useful to have different types on the same label as well:

    X: Final = "x"
    Y: Final = "y"
    Position = NamedTuple("Position", [(X, int), (Y, int)])
    DimensionName = NamedTuple("DimensionName", [(X, str), (Y, str)])
    
  6. tommyjcarpenter commented on Mar 7, 2022

    @tommyjcarpenter

    is there any update on this issue when Final is used? This seems last updated about two years ago

    I would expect this to be legal:

    In [3]: from typing import TypedDict, List, Final
    
    In [6]: X: Final = "namespaces_read"
    In [7]: Y: Final = "namespaces_write"
    
    In [4]: Permissions = TypedDict(
       ...:     "Permissions", {X: List[str], Y: List[str]}
       ...: )
    
    In [8]: Z: Permissions = {X: [], Y: []}
    
  7. tony commented on Jul 27, 2022

    @tony

    Let me know if you'd like me to remove this and create a separate issue if it's off scope. This falls under the canopy of TypedDict keys reuse, but it looks like this issue came to be about subsets, when I want reuse across keyed structures

    Allow all "typed fields" / keyed data structures to reuse their typings.

    e.g. typing.TypedDict, typing.NamedTuple and dataclasses.dataclass.

    Scenario: Typed exports from dataclass

    Assume my case, I want to offer users ability to export a typed tuple and dict from my Details dataclass, dataclasses.astuple and dataclasses.asdict

    #!/usr/bin/env python
    import dataclasses
    from typing import NamedTuple, TypedDict, get_type_hints
    
    
    class DetailsDict(TypedDict):
        name: str
        age: int
        address: str
    
    
    class DetailsTuple(NamedTuple):
        name: str
        age: int
        address: str
    
    
    @dataclasses.dataclass
    class Details:
        name: str
        age: int
        address: str
    
        def to_dict(self) -> DetailsDict:
            # return dataclasses.asdict(self, dict_factory=DetailsDict)
            return DetailsDict(**dataclasses.asdict(self))
    
        def to_tuple(self) -> DetailsTuple:
            # return dataclasses.astuple(self, tuple_factory=DetailsTuple)
            return DetailsTuple(*dataclasses.astuple(self))
    
    
    john = Details(name="John", age=25, address="123 Address St")
    print(john)
    print(john.to_dict())
    print(john.to_tuple())
    print(get_type_hints(john))
    print(get_type_hints(john.to_dict))
    print(get_type_hints(john.to_tuple))

    Output:

    Details(name='John', age=25, address='123 Address St')
    {'name': 'John', 'age': 25, 'address': '123 Address St'}
    DetailsTuple(name='John', age=25, address='123 Address St')
    {'name': <class 'str'>, 'age': <class 'int'>, 'address': <class 'str'>}
    {'return': <class '__main__.DetailsDict'>}
    {'return': <class '__main__.DetailsTuple'>
    

    Why would a user want to reuse dict/tuple/etc?

    • To maintain mypy strict = True compliance
      • inside of unit testing / assertions. In my case, I have typed pytest fixtures
    • They have an open source library and want tuples / dicts to be available for downstream use.
      • Spreading into functions *params, **params, e.g. loading data

      • Reuse in args/kwargs of function declarations, e.g.

        Details(*DetailsTuple(name='John', age=25, address='123 Address St'))
        Details(**DetailsDict(**{'name': 'John', 'age': 25, 'address': '123 Address St'}))

    Problem

    We have to redeclare the same field annotations for dict, tuple, and dataclass

    Solution

    We need a way to reuse the field typings across the different keyed data structures.

  8. nitedani commented on Sep 30, 2022

    @nitedani

    Why would a user want to reuse dict/tuple/etc?

    class CreateArgs(TypedDict):
        first_name: str
        last_name: str
        external_id: str
        address: str
        city: str
        phone: str
        zip: str
        email: str
        state: str
    
    
    class UpdateArgs(TypedDict, total=False):
        first_name: str
        last_name: str
        external_id: str
        address: str
        city: str
        phone: str
        zip: str
        email: str
        state: str
    type CreateArgs = {
      first_name: string;
      last_name: string;
      external_id: string;
      address: string;
      city: string;
      phone: string;
      zip: string;
      email: string;
      state: string;
    };
    
    type UpdateArgs = Partial<CreateArgs>;

    For me it's not about compliance, neither an open-source library.
    It's code repetition.
    I would like to define the keys only once.

  9. added a commit that references this issue on Dec 6, 2022
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