Repository navigation
typescript: a name destructured from a type-literal-typed value takes its member's type - #2011
Open
swapnilpaliwal-sd wants to merge 2 commits into
Open
swapnilpaliwal-sd wants to merge 2 commits into
swapnilpaliwal-sd wants to merge 2 commits into
Conversation
… its member's type
`const { port } = opts` with `opts: { port: Port }`, and `({ port }: Opts)` with
`type Opts = { port: Port }`, left `port` untyped, so `port.execute()` was
unresolved while `opts.port.execute()` beside it resolved. A type literal has no
declaration for param_type / expr_type to name: it is a shape. The destructuring
rules for parameters and variables now read the shape too (a shape is its own
scope), for fields, nested shapes and methods.
Case 100 covers inline and aliased literals, parameters and variables, a nested
shape and a shape method, with controls: an untyped source stays unresolved, and
a look-alike literal resolves to its own method, not to the interface. Case 75:
five destructured-prop calls now resolve to the targets the compiler oracle had
listed as known-missing (the shadowing guard still holds); they still show as
missing+extra only because a destructured caller is labelled once per bound name.
On a 1.9k-file Deno TypeScript repo: known_edge 7,410 -> 7,441, ambiguous_unknown
35,820 -> 35,789, 0 resolved edges lost; spot-checked against the source.
Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
swapnilpaliwal-sd
requested review from
JaredHLZhang,
Whua689 and
suyashpaliwal26
as code owners
November 3, 2026 17:23
The parser emits a row for a destructuring pattern and one per name it binds, all at
the pattern's position. normalize_edges.py turned each into a parameter, so
`aliasParam({ port, clock }: Opts)` was labelled `aliasParam(Opts,Opts,Opts)` while the
compiler oracle prints `aliasParam(Opts)`: the same edge showed as one missing and
one extra. One row per position, the pattern's when present.
Case 100's three known-missing edges and five of case 75's now agree with the oracle;
both debt lists are trimmed. Suite 110/110.
Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Symptom
A name destructured out of a value whose declared type is an object type literal stays untyped, so every call on it is
ambiguous_unknown, while the member access beside it resolves:Cause
The destructuring rules (
binding_pattern_type,var_binding_target) read the source's type (param_type/expr_type). A type literal has no declaration to be a type. It is a shape (param_shape_target/expr_shape), and nothing on the destructuring side read shapes.Fix
reference-types.dlgets a shape twin of each rule, for parameters (binding_pattern_shape) and variables (var_binding_shape), covering field types, nested shapes and methods. A shape is its own scope (scope_sibling(s, s)), so the existingdeclared_field/declared_methodlookups are reused. No negation is added.param_typegets one more positive clause, built only from rows that sit below the expression fixpoint.Tests
anysource stays unresolved; a look-alike literal{ execute(x) }resolves to its own method, not toPort.execute.normalize_edges.pylabelled a destructured parameter once per bound name (aliasParam(Opts,Opts,Opts); the compiler printsaliasParam(Opts)), so a right edge showed up as one missing plus one extra. It now keeps one row per position. Case 100 needs no known-missing file.Note for #2010: its case 99 lists three edges as known-missing only because of that label. Once both are in, those lines start passing and must be removed (re-bless 99 with
--oracle).Real project (1.9k-file Deno TypeScript repo)
Spot-checked new edges: an interface method reached through
const { transport } = optswithopts: { transport: Transport; ... }, and methods of an inlineutils: { json: { omit(..) } }parameter type.Stacks cleanly with #2010 (contextual destructured parameters); the two touch different rules.