Fix double-wrapped array type for list arguments with a typed prepare method#2668
Merged
amomchilov merged 1 commit intoJul 11, 2026
Conversation
… method GraphqlMutation/GraphqlInputObject would generate T::Array[T::Array[X]] for a list argument whose `prepare:` method has a sig, because the prepare method's return type (already the fully-coerced list value) was wrapped in T::Array[...] a second time.
5be839d to
4c68de7
Compare
amomchilov
approved these changes
Jul 10, 2026
Contributor
|
Good find, thanks! |
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.
Motivation
Tapioca::Dsl::Compilers::GraphqlMutation(andGraphqlInputObject, which shares the sameGraphqlTypeHelper.type_forhelper) generate an incorrect, doubly-nested array type for a list-typed GraphQL argument whoseprepare:method has a Sorbet sig.Given:
Tapioca currently generates:
instead of the correct:
Root cause
In
GraphqlTypeHelper#type_for, when aprepare_methodhas a valid sig, its return type unconditionally replacesparsed_type:A GraphQL
preparemethod receives (and returns) the argument's fully coerced value — including the list wrapper, when the argument type is a list. So when a prepare method's sig return type is used asparsed_type, it already reflects the full (possibly list) shape of the final value. The subsequentif type.list?block wraps it inT::Array[...]again, double-nesting it.This bug forces users to strip Sorbet sigs from otherwise well-typed
preparemethods just to get a correct RBI, which was the workaround applied in the wild that led me to find this (see Gusto/zenpayroll#356117).Fix
Track whether
parsed_typecame from aprepare_method's return type, and skip the extra list-wrap in that case, since the prepare method's return type is already final.Test plan
graphql_mutation_spec.rb:prepare:method whose sig returns the same list type (exercises the double-wrap bug directly)prepare:method that has no valid return type (void/untyped), confirming the existing fallback list-wrap behavior is untouched by the fixgraphql_input_object_spec.rbfor a list argument with aprepare:method whose sig returns the same list type, sinceGraphqlInputObjectshares the sameGraphqlTypeHelper.type_forcode path but previously had noprepare:coveragegraphql_mutation_spec.rbandgraphql_input_object_spec.rbsuites — all pass (24 tests, 74 assertions)prepare:method with a matchingT::Array[...]return sig) — regenerated RBI now producesT::Array[X]instead ofT::Array[T::Array[X]], and downstreamsrb tcpasses.