Description
The docs give very clear guidance on how to write code using unions of TypedDict
s:
https://mypy.readthedocs.io/en/stable/typed_dict.html#unions-of-typeddicts
This makes sense!
However, a common use case (for me) of a TypedDict
is to allow external code (e.g. REST API input) to be deserialized into a dict and refined at runtime into a TypedDict
-- which implies the the design requirement to have a branded field (e.g. tag
) must now exist in our public facing API.
This could work in some cases, but consider something like this:
from __future__ import annotations
from typing import List, TypedDict
class Policy(TypedDict):
name: str
class CombinedORPolicy(TypedDict):
OR: List[Policy]
class CombinedANDPolicy(TypedDict):
AND: List[Policy]
def print_policy(policy: CombinedANDPolicy | CombinedORPolicy | Policy) -> None:
if 'OR' in policy:
allowed_policy_names = ' '.join([p['name'] for p in policy['OR']])
print(f"Can match _any_ of the following: {allowed_policy_names}")
elif 'AND' in policy:
allowed_policy_names = ' '.join([p['name'] for p in policy['AND']])
print(f"Must match _all_ the following: {allowed_policy_names}")
else:
print(f"Must be exactly {policy['name']}")
# pretend this is an API endpoint or something
print_policy({'name': 'role:is_admin'})
#13838 being closed seems to imply this is possible but i'm still having trouble.
Here's the current mypy (1.14.1) output:
test.py: note: In function "print_policy":
test.py:16:68: error: TypedDict "CombinedANDPolicy" has no key "OR" [typeddict-item]
test.py:16:68: error: TypedDict "Policy" has no key "OR" [typeddict-item]
test.py:19:68: error: TypedDict "Policy" has no key "AND" [typeddict-item]
Found 3 errors in 1 file (checked 1 source file)
The solution as I understand is either:
- force an API change (e.g.
print_policy({'name': 'role:is_admin', 'tag': 'root-policy'})
) - use
cast
(e.g.policy = cast(CombinedANDPolicy, policy)
Is there anything more idiomatic i'm missing?
(I searched the tracker but wasn't able to find discission of this specifically, but I think this is related: #7981 #12098 #11080)
Is there a stance on this yet? Is this is a goal or non-goal of mypy? Thanks!
FWIW -- here's a version of this working in
thanks!