如何使用Sorbet抽象单例方法实现版本化Typed Struct反序列化
问题原因
这个报错是Sorbet对mixes_in_class_methods的类型处理规则导致的:通过该方法混合的类方法,只会挂载到包含模块的类的单例类上,既不属于VersionedStruct模块本身的实例方法,也不属于T.class_of(VersionedStruct)的方法,所以你直接cast到这两个类型都找不到对应的version和migrate方法。
解决方案
直接把类型指定为你定义的VersionedStruct::ClassMethods即可,这个模块里已经声明了所有抽象类方法的签名,所有包含VersionedStruct的类的单例类都会自动被Sorbet识别为该模块的子类型。
需要修改的代码只有两处:
- 修改
migrate私有方法的参数类型,把struct的类型从VersionedStruct换成VersionedStruct::ClassMethods load方法里的T.cast目标类型也同步换成VersionedStruct::ClassMethods
修改后的DataDeserializer代码如下:
class DataDeserializer sig { params(data_class: String, data_version: Integer, data: T::Hash[Symbol, T.untyped]).returns(T.any(MyStruct, MyOtherStruct, T::Struct)) } def load(data_class, data_version, data) struct_class = Object.const_get(data_class) migrated_data = if struct_class.include?(VersionedStruct) migrate(data_version, T.cast(struct_class, VersionedStruct::ClassMethods), data) else data end struct_class.new(migrated_data) end private sig { params(data_version: Integer, struct: VersionedStruct::ClassMethods, data: T::Hash[Symbol, T.untyped]).returns(T::Hash[Symbol, T.untyped]) } def migrate(data_version, struct, data) return data if data_version == struct.version struct.migrate(data) end end
另外补充一个业务代码的小问题:你现在写的MyStruct.migrate方法在v2_field存在时会返回nil,会导致后续new方法报错,建议改成return data if data[:v2_field]。
内容的提问来源于stack exchange,提问作者leifg
相关产品推荐
相关产品推荐

