Allow TypedDict construction with TypeName({...})

The following syntax works at runtime:

from typing import TypedDict

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

x = X({"a": 1, "b": ""})

But it is not documented anywhere in the typing spec or in the runtime documentation for the typing module, both of which only mention the syntax with X(a=1, b=""). However, both mypy and pyright support this syntax.

I propose that we should add this syntax to the spec as a supported part of the type system. It is widely supported, it’s in use in practice, and it’s useful at runtime for constructing a TypedDict with keys that aren’t valid identifiers.

There’s a detail to be worked out: what if you use both syntaxes at the same time? This works at runtime:

>>> X({"a": 1}, b="x")
{'a': 1, 'b': 'x'}

Mypy doesn’t allow it. Pyright does, but only if you specify all required keys in the positional argument. I don’t see a good reason to use this combined syntax, so my preference is to disallow it in the typing spec (no need to change the runtime) and say that users must pass either a single positional argument, or all required keyword arguments.

11 Likes

One use-case that comes to mind is composition of typed dicts:

from typing import TypedDict

class Y(TypedDict):
    a: int

class X(Y):
    b: str

y: Y = {"a": 1}
x: X = X(y, b="")  # currently not accepted

bpr playground

X({"a": 1}, b="x")

is really a syntax that doesn’t make sense to me. I’m also in favor of disallowing it in the typing spec. Type checkers can easily suggest

x: X = X(**{"a": 1}, b="x")

Fun fact I discovered

x: X = X({"a": 1} | {"b": "x"})

is not yet well supported by type checkers