Fix type inference for TypedDict unpacking in dictionary displays - #11611
Fix type inference for TypedDict unpacking in dictionary displays#11611Henry Su (hsusul) wants to merge 2 commits into
Conversation
|
🔒 Automated review in progress — Stella Huang (@StellaHuang95) is auto-reviewing this PR. |
| }); | ||
|
|
||
| if (!expectedTypedDictEntries) { | ||
| if (tdEntries.extraItems) { |
There was a problem hiding this comment.
Issue · Please address or respond
Changing this guard makes the block run when an expected TypedDict target is present. Closed TypedDicts populate extraItems with Never, so unpacking one into an expected TypedDict now contributes a non-literal str key that assignToTypedDict rejects, producing a new assignment error. Retain the context gate while removing only the fallback: if (!expectedTypedDictEntries && tdEntries.extraItems) { ... }.
|
|
||
| def test_multiple(td1: HomogeneousTD, td2: HeterogeneousTD, td3: OptionalTD): | ||
| res4 = {**td1, **td2, **td3} | ||
| reveal_type(res4, expected_text="dict[str, int | str | float]") |
There was a problem hiding this comment.
Warning · Non-blocking recommendation
The added cases do not cover the changed extraItems path or the expected-TypedDict context that currently regresses. Add coverage for unpacking a closed TypedDict and a PEP 728 extra_items TypedDict into a typed target, plus a non-strict inference case to cover the stated default-mode behavior.
|
Please update the corresponding Pylance evaluator and add product-level regression coverage; it still has the original optional-field guard and |
…losed/extra_items TypedDict coverage & fourslash test
|
Thank you for the detailed review Stella Huang (@StellaHuang95)! I have updated the PR with all requested changes:
|
Stella Huang (StellaHuang95)
left a comment
There was a problem hiding this comment.
Approved via Review Center.
Rich Chiodo (rchiodo)
left a comment
There was a problem hiding this comment.
Approved via Review Center.
Rich Chiodo (rchiodo)
left a comment
There was a problem hiding this comment.
Approved via Review Center.
Summary of Changes
When a
TypedDictinstance is unpacked inside a dictionary display (e.g.,{**td}or{**td1, **td2}) without an expectedTypedDicttarget type context (or understrictDictionaryInference), Pyright previously inferred the value type asUnknown.Cause
In
getKeyAndValueTypesFromDictionary, when evaluatingParseNodeType.DictionaryExpandEntryfor aTypedDictexpression:knownItemswere only added tokeyTypesandvalueTypesifentry.isRequired || entry.isProvided. However, outside an expectedTypedDictcontext (whereisProvidedis false),isRequiredorisProvidedchecks excluded optional fields from key/value inference.expectedTypedDictEntrieswas undefined,getObjectType()(object) was unconditionally appended tovalueTypesas a fallbackextraItems?.valueType. When combined with other types withoutstrictDictionaryInference,combineTypesfell back toUnknownor broadened toobject.Fix
Updated
getKeyAndValueTypesFromDictionaryintypeEvaluator.ts:knownItemswhenexpectedTypedDictEntriesis undefined (!expectedTypedDictEntries || entry.isRequired || entry.isProvided).extraItemstokeyTypes/valueTypesiftdEntries.extraItemsis explicitly present, avoiding pushing an unnecessaryobjectfallback type.Test Coverage
packages/pyright-internal/src/tests/samples/typedDict28.pycovering unpacking homogeneous, heterogeneous, optional, and multipleTypedDictinstances into dictionary displays.TypedDict28topackages/pyright-internal/src/tests/typeEvaluator7.test.ts.Validation Results
npm run check: Passed (Syncpack lint, ESLint, Prettier)npm run typecheck: Passed (tsc --noEmitacross all workspace packages)npx jest src/tests/typeEvaluator7.test.ts: 168 tests passedgit diff --check: Passed cleanly with no whitespace errors