我应该如何管理 gem 中的冰糕错误,其中类型信息必须在 rbi 文件中而不是内联?

How should I manage sorbet errors in a gem where type info must be in rbi files and not inline?

我维护 pdf-reader ruby gem 并用它来试验冰糕。我以前没有吃冰糕的经验。

我想使用类型来改善开发体验,并使用 gem 分发类型信息,以便使用 sorbet 的下游用户可以受益。但是,我想避免添加运行时 sorbet 依赖项。大多数下游用户不使用 sorbet,他们不应该获得新的运行时依赖。

我认为这意味着我应该将类型信息作为 *.rbi 文件分发到顶级 rbi/ 目录中。我无法将类型内联到我的源代码中(extend T::Sig,等等)。

在开发(和 test/ci)期间,rbi/*.rbi 中的类型信息对于静态类型检查很有用。但是我不能依赖在运行时正确的类型(下游用户可能会传递不同的类型),所以在某些情况下我仍然想 confirm the type like this:

def initialize(runs, mediabox)
  raise ArgumentError, "a mediabox must be provided" if mediabox.nil?

...即使我的 rbi 文件声明 mediabox 永远不能为 nil:

sig { params(runs: T::Array[PDF::Reader::TextRun], mediabox: T::Array[Numeric]).void }
def initialize(runs, mediabox); end

.. 但是 sorbet 对代码不满意:

$ srb tc
./lib/pdf/reader/page_layout.rb:20: This code is unreachable https://srb.help/7006
    20 |      raise ArgumentError, "a mediabox must be provided" if mediabox.nil?
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    ./lib/pdf/reader/page_layout.rb:20: This condition was always falsy (T::Boolean)
    20 |      raise ArgumentError, "a mediabox must be provided" if mediabox.nil?
                                                                    ^^^^^^^^^^^^^
  Got T::Boolean originating from:
    ./lib/pdf/reader/page_layout.rb:20:
    20 |      raise ArgumentError, "a mediabox must be provided" if mediabox.nil?
                                                                    ^^^^^^^^^^^^^
Errors: 1

我可以明确忽略该错误:

$ srb tc --suppress-error-code 7006
No errors! Great job.

有什么方法可以保持运行时类型检查而不会出现 sorbet 抱怨,并且不会忽略错误?或者也许“冰糕方式”只是删除运行时检查并在运行时没有它生活?

或者我关于仅使用 rbi 文件作为类型信息的假设是错误的?

添加辅助验证方法:

sig { params(obj: Object, cls: Module).void }
def self.validate(obj, cls)
  raise ArgumentError, "#{obj} must be a #{cls}" unless obj.is_a?(cls)
end

现在您可以在整个代码中验证类型,而无需添加 sorbet-runtime 依赖项。

您当然也可以引入变体,例如 validate_not_null(对于上面的特定示例)。对于冰糕特定类型(例如 T::Array),它确实有点棘手,具体取决于您希望验证的严格程度。