fix(types): make request Refs covariant#4585
Draft
damusix wants to merge 1 commit into
Draft
Conversation
Typed lifecycle methods failed to attach to any surface whose Refs were not identical. Method is now bivariant, Request/ResponseToolkit covariant with an empty-bag default; paramsArray was mistyped as keys.
damusix
force-pushed
the
fix/reqref-variance
branch
from
July 21, 2026 18:37
0d734b2 to
b558579
Compare
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.
Typed routes, lifecycle methods, pres, failActions, and extensions currently error whenever their
Refsare not 100% identical — the limitation documented intest/types/index.ts("Request not assignable to Request"). Three compounding causes:RequestembedsLifecycle.Method<Refs>throughroute.settings, whose contravariantrequestparameter makesRefseffectively invariant — even plain widening (Request<{Payload: X}>→Request) fails.keyofpositions (paramsArray, preassign) additionally block covariance.paramsArraywas also mistyped: it holds the param values in path order, so it isstring[].Request<A>toRequest<B>by comparing the type arguments directly, and a partialRefsobject can never satisfy the fullReqRefDefaultsinterface. The type parameter therefore now defaults to{}— reads are unchanged, sinceMergeRefs<{}>resolves to the same defaults.The fix:
Lifecycle.Methodis declared through a method signature so comparisons are bivariant (the same technique React's typings use for event handlers),Request/ResponseToolkitare annotatedoutwith the{}default, and the twokeyofpositions are made covariant-safe (assignkeeps declared-key autocomplete viaExtract<keyof ...> | (string & {})).Net effect: a method typed with narrower refs attaches to any surface typed with the defaults (
server.ext(), routeext,failAction, pre on untyped routes), and a route typed with a superset of refs accepts methods typed with any subset — while contradictory refs and default-to-narrow flows still error. Types-only; no runtime change.