Skip to content

Commit a4fe82b

Browse files
committed
Require string-backed TypedDict enum keys
TypedDict indexing treated any string-valued LiteralType as a string literal, including ordinary enum members whose literal value stores the member name. Require the literal fallback to be a subtype of str, while preserving StrEnum and string-mixin enum keys. Fixes #21722.
1 parent 2e6c87e commit a4fe82b

2 files changed

Lines changed: 27 additions & 1 deletion

File tree

mypy/checkexpr.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4817,7 +4817,7 @@ def visit_typeddict_index_expr(
48174817
if (
48184818
isinstance(key_type, LiteralType)
48194819
and isinstance(key_type.value, str)
4820-
and key_type.fallback.type.fullname != "builtins.bytes"
4820+
and is_subtype(key_type.fallback, self.named_type("builtins.str"))
48214821
):
48224822
key_names.append(key_type.value)
48234823
else:

test-data/unit/check-typeddict.test

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3004,6 +3004,32 @@ d[True] # E: TypedDict key must be a string literal; expected one of ("foo")
30043004
[builtins fixtures/dict.pyi]
30053005
[typing fixtures/typing-typeddict.pyi]
30063006

3007+
[case testTypedDictEnumKey]
3008+
# flags: --python-version 3.11
3009+
from enum import Enum, StrEnum
3010+
from typing import TypedDict
3011+
3012+
class PlainEnum(Enum):
3013+
foo = "foo"
3014+
3015+
class StringEnum(StrEnum):
3016+
foo = "foo"
3017+
3018+
class StringMixinEnum(str, Enum):
3019+
foo = "foo"
3020+
3021+
class TD(TypedDict):
3022+
foo: int
3023+
3024+
d: TD
3025+
d[PlainEnum.foo] # E: TypedDict key must be a string literal; expected one of ("foo")
3026+
d[PlainEnum.foo] = 1 # E: TypedDict key must be a string literal; expected one of ("foo") \
3027+
# E: Argument 1 to "__setitem__" has incompatible type "PlainEnum"; expected "str"
3028+
reveal_type(d[StringEnum.foo]) # N: Revealed type is "builtins.int"
3029+
reveal_type(d[StringMixinEnum.foo]) # N: Revealed type is "builtins.int"
3030+
[builtins fixtures/enum.pyi]
3031+
[typing fixtures/typing-typeddict.pyi]
3032+
30073033
[case testTypedDictUppercaseKey]
30083034
from typing import TypedDict
30093035

0 commit comments

Comments
 (0)