Sorbet中是否存在等价于TypeScript Pick的类型工具?
Sorbet 中实现 TypeScript
Pick 等效能力的方案 Sorbet 作为名义类型系统,原生没有提供和 TypeScript 结构类型体系下的 Pick 完全对等的内置工具类型,针对 T::Struct 返回字段子集的场景,有两种可落地的类型安全实现方式:
手动定义子集对应的
T::Struct类型
这是 Sorbet 官方推荐的标准写法,完全符合 Sorbet 的类型设计范式,类型检查可以做到100%覆盖:# 原结构体定义 class User < T::Struct const :id, Integer const :name, String const :mobile, String const :internal_tag, String end # 等效于 Pick<User, 'id' | 'name'> 的公开信息类型 class UserPublicProfile < T::Struct const :id, Integer const :name, String end sig { params(user: User).returns(UserPublicProfile) } def extract_public_profile(user) UserPublicProfile.new( id: user.id, name: user.name ) end该方案没有元编程开销,类型校验严格,唯一的缺点是如果需要的字段子集组合较多,会产生额外的结构体定义代码。
封装通用选取方法 + 泛型标注
如果需要减少重复的结构体定义,可以封装通用的字段选取工具方法,结合 Sorbet 泛型做类型约束,适合对类型严格度要求不极致的场景:module StructUtils extend T::Sig extend T::Generic sig { type_parameters(:Source, :PickedKey) .params( source: T.all(T::Struct, T.type_parameter(:Source)), picked_keys: T::Array[T.type_parameter(:PickedKey)] ) .returns(T::Hash[T.type_parameter(:PickedKey), T.untyped]) } def self.pick_fields(source, picked_keys) serialized = source.serialize(stringify_keys: false) picked_keys.to_h { |key| [key, serialized[key]] } } end # 调用示例 user = User.new(id: 2, name: "Li Si", mobile: "13xxxxxxxxx", internal_tag: "test") public_data = StructUtils.pick_fields(user, [:id, :name])注意:受 Sorbet 类型系统能力限制,该方案无法自动推导选中字段对应值的精确类型,返回值的值类型会被标记为
T.untyped,如果需要强类型校验,优先选择第一种手动定义结构体的方案。
补充说明:不要尝试用元编程动态生成 T::Struct 子类来模拟自动派生 Pick 类型,这类动态定义的结构无法被 Sorbet 静态扫描识别,会直接丢失类型检查能力。
内容的提问来源于stack exchange,提问作者Impirator
相关产品推荐
相关产品推荐

