我应该如何管理 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
),它确实有点棘手,具体取决于您希望验证的严格程度。
我维护 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
),它确实有点棘手,具体取决于您希望验证的严格程度。