Skip to content

Positive TypedDict key membership does not make key access safe #3461

@dsfaccini

Description

@dsfaccini

hey guys, I'm using this fork dsfaccini/ruff@main...codex/ty-pydantic-compat to type check pydantic-ai locally. It has patches for all the diagnostics we get from ty in pydantic-ai

during the process of writing the patches and getting our errors to zero (from 540) I had codex take notes and now I'm going through those notes with subagents to open issues on the more impactful changes (trying to make sure as best as we can that it is actually a valid issue and that there's not already an issue tracking it; if there is I apologize 😅)

sending you best and thank you for your work!
David

Ps. I'm very excited about ty so I'll be tracking new diagnostics in our repo pydantic/pydantic-ai#5428 going forward, so we can test the patches long-term


A positive literal-key membership check on a TypedDict union does not make access to that key safe.

The branch should not necessarily narrow the whole value to the TypedDict variant that declares the key, since open TypedDicts can have extra keys. But after "bytes" in file_content, indexing file_content["bytes"] should be safe.

Playground link: https://play.ty.dev/fa50d140-6a86-4415-9069-c0ca40164ffd

Repro (no external deps)

from typing import TypedDict


class FileWithBytes(TypedDict):
    bytes: bytes


class FileWithUri(TypedDict):
    uri: str


def get_bytes(file_content: FileWithBytes | FileWithUri) -> bytes:
    if "bytes" in file_content:
        return file_content["bytes"]
    raise ValueError

Run:

uvx ty check ty_repro_typed_dict_key_membership.py

Actual result

error[invalid-key]: Unknown key "bytes" for TypedDict `FileWithUri`
  --> ty_repro_typed_dict_key_membership.py:14:16
   |
14 |         return file_content["bytes"]
   |                ------------ ^^^^^^^ Unknown key "bytes"
   |                |
   |                TypedDict `FileWithUri` in union type `FileWithBytes | FileWithUri`
   |

Expected result

No diagnostic. In the positive branch, the membership check establishes that the key exists, so literal key access should be accepted.

Notes

ty 0.0.35 via uvx ty check.

A Pyright-style filter to only the union members that declare the key would be too strong for open TypedDicts: an open FileWithUri value may legally contain an extra bytes key. A more precise behavior would be to narrow the positive branch to something like the original type intersected with a synthesized TypedDict requiring key bytes, so key access is safe without pretending the value must be FileWithBytes.

Metadata

Metadata

Assignees

Labels

narrowingrelated to flow-sensitive type narrowingtypeddict
No fields configured for Feature.

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions