Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions lib/tapioca/runtime/generic_type_registry.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,15 @@ def initialize(raw_type, underlying_type)
def valid?(obj)
obj.is_a?(@underlying_type)
end

# `T::Types::Base#recursively_valid?` checks the clone `raw_type` instead of
# routing through `valid?`, so delegate to keep composite (`T.all`, `T::Hash`)
# and `T::Props` member validation consistent with the `valid?` override above.
# @override
#: (untyped obj) -> bool
def recursively_valid?(obj)
valid?(obj)
end

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess it's okay to allow everything while running tapioca dsl? There's a similar comment on create_generic_type below.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, the full rationale already lives on create_generic_type's <= ("we act like all subtype checks pass", for serialization rather than runtime enforcement), so I left this as a one-line cross-reference to it instead of duplicating. And yes, allowing everything is fine here since tapioca dsl only serializes sigs, it doesn't enforce them at runtime.

end

class << self
Expand Down
13 changes: 13 additions & 0 deletions spec/tapioca/runtime/generic_type_registry_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,19 @@ class GenericTypeRegistrySpec < Minitest::Spec
T.let(SampleGenericInterfaceImplementation.new, SampleGenericInterface[Object])
end

it "recognizes underlying-type instances via recursively_valid? in composite/T::Props members (issue #2130)" do
struct_class = T.let(
Class.new(T::Struct) do
const :members, T::Hash[Symbol, SampleGenericInterface[Object]]
end,
T.untyped,
)

instance = struct_class.new(members: { key: SampleGenericInterfaceImplementation.new })

assert_instance_of(SampleGenericInterfaceImplementation, instance.members.fetch(:key))
end

it "does not reuse generic instances for redefined constants with the same name" do
first_constant, first_generic_type = register_reloadable_generic

Expand Down
Loading