Type event handler calls with five to eight positional values

AD
7h ago
+145-0

Gap

EventCallback.__call__ has overloads that bind at most four values. Handlers of five or more arguments only had overloads for binding their first one to four, so calling such a handler with all its arguments (e.g. State.update_file_tree(False, False, False, False, False), or seven positional values) matched no overload under ty or pyright, although the runtime takes any number of positional and keyword arguments.

Change

For each count of five to eight values, two overloads:

  • a receiver of exactly that arity (EventCallback[V, ..., Vn]) returning EventCallback[()]. A receiver with an Unpack[Q] tail is not enough here: ty then solves V from the value rather than the receiver (astral-sh/ty#4657), so a Var[int] passed for an int argument is rejected. This is the same mitigation the one-to-four overloads use.
  • a longer receiver (EventCallback[V, ..., Vn, Vn+1, *Q]) returning EventCallback[Vn+1, *Q], for binding the leading arguments. It needs one more argument than the exact overload, so each receiver still matches one overload per number of values and a wrong value is reported against its parameter.

Not done: keyword arguments (State.handler(a, b, flag=True)). EventCallback[*P] carries the handler's argument types but not their names, so a keyword can't be checked against the parameter it names. An overload taking **kwargs: Any would accept any keyword, misspelled ones included, and could not say which arguments remain unbound. Typing keywords soundly would mean carrying a ParamSpec (or the names) in EventCallback, which is a design change, not an overload. Also unchanged: partial binding of a 5+ argument handler with a Var value still trips the ty bug, as it does today for one to four values.

Before (tests/type_checking/event.py with the new examples, against main)

$ uv run ty check --python-version 3.14 tests/type_checking/event.py
error[no-matching-overload]: No overload matches arguments   (x4)
error[type-assertion-failure]: ... Inferred type is `Unknown` (x4)
Found 8 diagnostics

$ uv run pyright tests/type_checking/event.py
event.py:46:13 - error: No overloads for "__call__" match the provided arguments
event.py:48:5 - error: No overloads for "__call__" match the provided arguments
event.py:59:13 - error: No overloads for "__call__" match the provided arguments
event.py:60:13 - error: No overloads for "__call__" match the provided arguments
8 errors

After

$ uv run ty check --python-version 3.14 tests/type_checking/
All checks passed!
$ uv run pyright reflex tests packages/reflex-base
0 errors, 2 warnings (pre-existing, tests/units/test_var.py)

A wrong value is still reported against its parameter, e.g. pyright: Argument of type "Literal['x']" cannot be assigned to parameter "value6" of type "int | Var[int]". tests/units/test_event.py tests/units/reflex_base: 1471 passed.

🤖 Generated with Claude Code