pythonenumspython-typingtypeddict

Creating a TypedDict with enum keys


I am trying to create a TypedDict for better code completion and am running into an issue.

I want to have a fixed set of keys (an Enum) and the values to match a specific list of objects depending on the key.

For example:

from enum import Enum


class OneObject:
    pass

class TwoObject:
    pass

class MyEnum(Enum):
    ONE: 1
    TWO: 2

I am looking to have something like this:

from typing import TypedDict


class CustomDict(TypedDict):
    MyEnum.ONE: list[OneObject]
    MyEnum.TWO: list[TwoObject]

However, I am getting Non-self attribute could not be type hinted and it doesn't really work. What are my options?


Solution

  • This is not compatible with the TypedDict specification as laid out in PEP 589. Let me quote: (emphasis mine)

    A TypedDict type represents dictionary objects with a specific set of string keys, and with specific value types for each valid key.

    So using arbitrary enum members for defining TypedDict keys is invalid.

    While TypedDict does also support an alternative, functional definition syntax and you could theoretically make your enum have the str data type by doing class MyEnum(str, Enum): ..., you would still probably not be able to define a TypedDict with those enum members in a way that your type checker understands.

    That is because only actual string literals are officially accepted as keys as mentioned in the section on the Use of Final Values and Literal Types. Quote: (again, emphasis mine)

    Type checkers are only expected to support actual string literals, not final names or literal types, for specifying keys in a TypedDict type definition. [...] The motivation for this is to make type declarations self-contained, and to simplify the implementation of type checkers.

    In other words, whether something like the following is supported depends entirely on any given type checker:

    from enum import Enum
    from typing import TypedDict
    
    
    class OneObject:
        pass
    
    
    class TwoObject:
        pass
    
    
    class MyEnum(str, Enum):
        ONE = "1"
        TWO = "2"
    
    
    CustomDict = TypedDict(
        "CustomDict",
        {MyEnum.ONE: list[OneObject], MyEnum.TWO: list[TwoObject]}
    )
    

    Mypy (currently) does not and gives the output: error: Invalid TypedDict() field name. (By the way, I tested it with Final variables as keys and those are also rejected.)

    So depending on what your use case is, you will probably have to bite the bullet and explicitly type out the enum/key names again or just not use an enum for that in the first place, as suggested by @artem in his answer.