-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Guidance needed: how to narrow unions of TypedDicts? #18543
Comments
Adding |
For reference, I checked: from __future__ import annotations
from typing import List, TypedDict, final
@final
class Policy(TypedDict):
name: str
@final
class CombinedORPolicy(TypedDict):
OR: List[Policy]
@final
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'}) and it passes without mypy complaining. |
Brilliant, thanks! I might send a PR to add this to the docs here https://mypy.readthedocs.io/en/stable/typed_dict.html#unions-of-typeddicts |
magicmark
added a commit
to magicmark/mypy
that referenced
this issue
Jan 27, 2025
Fixes python#18543 This is a useful but also non-obvious pattern that seems worthy of a docs example :)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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 aTypedDict
-- 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:
#13838 being closed seems to imply this is possible but i'm still having trouble.
Here's the current mypy (1.14.1) output:
The solution as I understand is either:
print_policy({'name': 'role:is_admin', 'tag': 'root-policy'})
)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!
The text was updated successfully, but these errors were encountered: