Skip to content

typescript: a destructured parameter typed by its context, and a literal nested in a typed literal - #2010

Open
swapnilpaliwal-sd wants to merge 1 commit into
0.2.3from
fix/ts-contextual-destructured-params
Open

swapnilpaliwal-sd wants to merge 1 commit into
0.2.3from
fix/ts-contextual-destructured-params

Conversation

@swapnilpaliwal-sd

Copy link
Copy Markdown
Contributor

What was wrong

An arrow whose parameter type comes from its context, not an annotation, lost every call on a name destructured from that parameter:

const poll: (ctx: Ctx) => R = async ({ data, utils }) => utils.http(data);    // utils.http: unresolved
const poll2: (ctx: Ctx) => R = async (ctx) => ctx.utils.http(ctx.data);       // resolved

define({ lifecycle: { start: async ({ utils }) => utils.http(..) } });          // unresolved
define({ lifecycle: { start: async (ctx) => ctx.utils.http(..) } });            // unresolved too

Causes and fix (contextual-params.dl)

  1. Destructured contextual parameter. A bound name reads its pattern's type through param_type (binding_pattern_type), which only an annotation fills. Contextual typing (lambda_param_type) types references to the parameter, never the names destructured from it. Now each bound name takes the type and shape of the field it names on the contextual type. Its uses are typed next to the plain parameter's uses (expr_type / expr_shape), not through param_type, because overload rejection reads param_type under negation.
  2. Literal nested in a typed literal. objlit_ctx_ref covered a literal that is a variable initializer, a field initializer, a returned value or a call argument, but not a literal that is the property value of a typed literal. A nested literal now takes the type of the field it fills, which also works through intersections (Omit<..> & { lifecycle?: Slots }).

Tests

  • New case 99-contextual-destructured-param covers the const-annotated arrow, a nested slot under an intersection-typed parameter, and arrows written both destructured and plain. Controls: an any pattern and a parameter typed by an object type literal both stay unresolved.
    • Oracle: 3 agree. Three entries in known-missing are a caller-label artifact only: the engine labels a destructured function (?,?,?) where the compiler writes (?). The pre-existing annotated function has the same artifact. One real gap is recorded: the type-literal parameter.
  • run-tests.sh: the other 109 cases are unchanged.

On a real repository (a Deno connector monorepo, ~750 endpoint definitions)

before after
known_edge 7,410 8,923
ambiguous_unknown 35,820 34,300
multi_inferred 63 77
edges lost — 0
hook calls of the framework's HTTP helper resolved 0 / 198 194 / 198

The new edges are the expected interface members (JsonUtil.* 495+347+…, HookLogger.* 325, the HTTP helper 209). 7 sites now also reach a class whose owned method has the same shape as the interface member. That comes from the existing structural dispatch, which these sites now reach; it is not a new rule.

Not covered

A parameter typed by an alias to an object type literal (type Ctx = { utils: Utils }): the bound name reads only a declaration's fields.

…ral nested in a typed literal

`const poll: (ctx: Ctx) => R = async ({ data, utils }) => utils.http(..)` and
`define({ lifecycle: { start: async ({ utils }) => utils.http(..) } })` left every call
on `utils` unresolved, while `ctx.utils.http(..)` on the same arrow resolved.

Two causes:
- a bound name reads its pattern's type through param_type, which only an annotation
  fills; contextual typing (lambda_param_type) typed references to the parameter and
  never the names destructured from it. The bound name now takes the type (and shape)
  of the field it names from the contextual type, typed at its uses beside the plain
  parameter's, not through param_type, which overload rejection reads under negation.
- an object literal written as a property of a typed literal had no scope, so functions
  in a nested slot took no parameter types. It now takes the type of the field it fills.

Case 99 pins both, with controls: an `any` pattern and a parameter typed by an object
type literal stay unresolved. Every other TypeScript case is unchanged (109/109).

On a Deno connector repository (~750 endpoint definitions): known_edge rows 7,410 -> 8,923,
ambiguous_unknown 35,820 -> 34,300, multi_inferred 63 -> 77, no edge lost; 194 of 198
hook calls of the framework's HTTP helper now resolve.

Co-authored-by: axiomcode-bot[bot] <334110751+axiomcode-bot[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working typescript TypeScript

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant