You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Sorbet中为T::Struct运行时动态定义prop且避免类型检查报错

问题根因

Sorbet 是静态类型检查工具,不会执行你的运行时 register_props 逻辑,所以无法识别动态添加的 prop 属性,自然会认为 MyStruct 的构造函数不需要任何入参,触发参数数量不匹配的报错。

可行解决方案

  • 方案1:补充 RBI 接口文件(最优推荐)

    RBI 是 Sorbet 专用的类型接口文件,专门用于给静态检查补充运行时才会定义的类型信息。你可以在项目的 Sorbet RBI 目录下新增 my_struct.rbi 文件,手动声明所有你会动态注册的属性:

    # 这个文件仅用于 Sorbet 静态类型检查,不影响运行时逻辑
    class MyStruct < T::Struct
      prop :foo, T.untyped
      prop :bar, T.nilable(T.untyped)
    end
    

    该方案优势在于静态检查会完全识别这些属性的存在,既可以校验构造函数入参,也可以校验后续属性调用的正确性,和你写静态prop的体验完全一致,运行时的动态注册逻辑也不会和RBI声明冲突。

  • 方案2:重写构造函数类型签名,适配动态属性

    如果你要注册的prop是完全动态的,无法提前枚举写RBI,可以直接在MyStruct里重写initialize的类型签名,允许接收任意关键字参数:

    # typed: true
    require 'sorbet-runtime'
    
    class MyStruct < T::Struct
      MyPropType = T.type_alias { T::Hash[Symbol, Class] }
      
      extend T::Sig
      sig { params(kwargs: T.untyped).void }
      def initialize(**kwargs)
        super(**kwargs)
      end
    
      class << self
        extend T::Sig
    
        sig { params(props: MyPropType).void }
        def register_props(props)
          props.each do |prop_name, prop_type|
            prop(prop_name, prop_type)
          end
        end
      end
    end
    

    该方案下Sorbet不会再校验构造函数的入参数量和名称,运行时T::Struct本身还是会校验你传入的key是否已经注册、类型是否匹配,仅会丢失静态检查阶段的参数校验能力。

  • 方案3:调用构造函数时临时绕过静态检查(不推荐)

    如果不想修改原有类的实现,可以在每次创建实例时用T.unsafe绕过检查:

    my_struct = T.unsafe(MyStruct).new(foo: 'foo', bar: Bar.new)
    

    该方案改动最小,但每次调用都要加T.unsafe,代码冗余度高,也完全丢失了静态检查的能力。

内容的提问来源于stack exchange,提问作者Waseem

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 16:54:10