🎃
Sorbetのruntimeでの型チェック抑制方法まとめ
最近Sorbetを使い始めたのでメモっておきます。
Rubyの型チェックツールであるSorbetはruntimeでの型チェックができることが一つの特徴です。
ただし、型導入時に設定をミスると本番環境でエラーになってしまいます。
そこでエラーの抑制方法について理解を深めるためにコードとともに挙動を見てみます。
※ 公式のドキュメントのRuntime Configurationも合わせてご覧ください。
コードによるRuntime Configuration
Runtimeでの型チェックはコードの設定によって挙動を変えることができます。
項目ごとに簡単に説明します。
default_checked_level
- sigに.checkedを指定してない場合にruntimeでの型チェックを実行するかどうか
:always
(default),:tests
,:never
- ただし、これで抑制できるのはsig と実際の値が合わない(call_validation_error)だけ
- 環境変数
SORBET_RUNTIME_DEFAULT_CHECKED_LEVEL
でも設定可能
T::Configuration.default_checked_level = :tests
enable_checking_for_sigs_marked_checked_tests
- .checked(:tests)を指定している場合も通常のsigと同じに扱うかどうか
- 環境変数
SORBET_RUNTIME_ENABLE_CHECKING_IN_TESTS
でも設定可能
T::Configuration.enable_checking_for_sigs_marked_checked_tests
inline_type_error_handler
- T.let, cast, must, assert_type! で間違った変換をしている場合(inline type assertions)のエラーハンドリング
T::Configuration.inline_type_error_handler = lambda do |error, opts|
puts error.message
end
call_validation_error_handler
- sig と実際の値が合わない場合(invalid method calls)のエラーハンドリング
T::Configuration.call_validation_error_handler = lambda do |signature, opts|
puts opts[:pretty_message]
end
sig_builder_error_handler
- sigのproc{}部分でreturnsやvoidを指定していないなどの文法ミスがあった場合(invalid sig procs)のエラーハンドリング
T::Configuration.sig_builder_error_handler = lambda do |error, location|
puts error.message
end
sig_validation_error_handler
- sig自体の文法は正しいが、overrideやabstractの指定と実態が異なる場合(invalid sigs)のエラーハンドリング
T::Configuration.sig_validation_error_handler = lambda do |error, opts|
puts error.message
end
コード例
# typed: true
require 'sorbet-runtime'
# config
T::Configuration.default_checked_level = :always
T::Configuration.enable_checking_for_sigs_marked_checked_tests
T::Configuration.inline_type_error_handler = lambda do |error, opts|
puts "\n--- inline_type_error_handler ---\n#{error.message}\n"
end
T::Configuration.call_validation_error_handler = lambda do |signature, opts|
puts "\n--- call_validation_error_handler ---\n#{opts[:pretty_message]}\n"
end
T::Configuration.sig_builder_error_handler = lambda do |error, location|
puts "\n--- sig_builder_error_handler ---\n#{error.message}\n"
end
T::Configuration.sig_validation_error_handler = lambda do |error, opts|
puts "\n--- sig_validation_error_handler ---\n#{error.message}\n"
end
class Sample
extend T::Sig
# T.letでStringの値をIntegerに変換しようとしている
# -> inline_type_error_handler
sig { params(num: Integer).returns(Integer) }
def a(num)
T.let('string', Integer)
1
end
# sig内でStringを返すと指定しているが、実際はIntegerが返ってくる
# -> call_validation_error
sig { params(num: Integer).returns(String) }
def b(num)
1
end
# sig内のprocで.returnsや.voidを指定してない
# -> sig_builder_error_handler
sig { params(num: Integer) }
def c(num)
1
end
# .overrideと書いているが、実際はoverrideしていない
# -> sig_validation_error_handler
sig { params(num: Integer).override.returns(Integer) }
def d(num)
1
end
end
Sample.new.a(1)
Sample.new.b(1)
Sample.new.c(1)
Sample.new.d(1)
これを実行すると、以下のようにエラーが出力されます。
$ ruby lib/sample.rb
--- inline_type_error_handler ---
T.let: Expected type Integer, got type String with value "string"
Caller: lib/sample.rb:44
--- call_validation_error_handler ---
Return value: Expected type String, got type Integer with value 1
Caller: lib/sample.rb:68
Definition: lib/sample.rb:50 (Sample#b)
--- sig_builder_error_handler ---
You must provide a return type; use the `.returns` or `.void` builder methods.
--- sig_validation_error_handler ---
You marked `d` as .override, but that method doesn't already exist in this class/module to be overridden.
Either check for typos and for missing includes or super classes to make the parent method shows up
... or remove .override here: Sample at lib/sample.rb:62
ハンドラーの設定自体をコメントアウトすれば、runtimeエラーが出力されます。
参考になれば幸いです。
Discussion