When attempting to serialize a model attribute to a specific type[0], sorbet-rails generates generic type signatures as if that attribute were unknown [1]. That shouldn't be the case, given that we are explicitly serializing & deserializing to a given class.
I am proposing that we pass that directly to the RBI definitions[2]. Given these examples, this will allow us to do
model = Model.first
model.data.a_prop
rather than
model = Model.first
data = T.cast(model.data, Serialized)
data.a_prop
[0] sample model definition
class Model < ActiveRecord::Base
class Serialized < T::Struct
sig { params(data: T.nilable(T::Hash[String, String])).returns(T.self_type) }
def load(data)
TypeCoerce[self].new.from(data)
end
sig { params(data: T.untyped).returns(T.nilable(T::Hash[String, String])) }
def dump(data)
data.serialize
end
prop :a_prop, T.nilable(String)
end
# ie. data's DB column is a Postgres JSONB
serialize :data, Serialized
end
[1] generated types before this PR
sig { returns(T.any(T::Array[T.untyped], T::Boolean, Float, T::Hash[T.untyped, T.untyped], Integer, String)) }
def data; end
sig { params(value: T.any(T::Array[T.untyped], T::Boolean, Float, T::Hash[T.untyped, T.untyped], Integer, String)).void }
def data=(value); end
sig { returns(T::Boolean) }
def data?; end
[2] generated types after this PR
sig { returns(Model::Serialized) }
def data; end
sig { params(value: Model::Serialized).void }
def data=(value); end
sig { returns(T::Boolean) }
def data?; end
When attempting to serialize a model attribute to a specific type[0], sorbet-rails generates generic type signatures as if that attribute were unknown [1]. That shouldn't be the case, given that we are explicitly serializing & deserializing to a given class.
I am proposing that we pass that directly to the RBI definitions[2]. Given these examples, this will allow us to do
rather than
[0] sample model definition
[1] generated types before this PR
[2] generated types after this PR