TL;DR
- RubyKaigiで型チェックをしてくれるgemが発表されていた
- おもしろそうなので使ってみた
- エディタに組み込まれたらもう普通に使えそうな気がする
使い方
class Person # @dynamic name, contacts attr_reader :name, :contacts def initialize(name:) @name = name @contacts = [] end def guess_country() contacts.map do |contact| case contact when Phone contact.country end end.compact.first end end class Phone # @dynamic country, number attr_reader :country, :number def initialize(country:, number:) @country = country @number = number end def ==(other) if other.is_a?(Phone) other.country == country && other.number == number end end def hash self.class.hash ^ country.hash ^ number.hash end end
# 型定義ファイル class Person @name: String @contacts: Array<Phone> def initialize: (name: String) -> any def name: -> String def contacts: -> Array<Phone> def guess_country: -> (String | nil) end class Phone @country: String @number: String def initialize: (country: String, number: String) -> any def country: -> String def number: -> String end
bundle exec steep check -I test.rbi test.rb # 何も問題がなければ何も出ない
問題がある場合
phone = Phone.new(country: 'Japan', number: 1)
こんな感じでstring型が期待される場所に、integerを渡してみた.
➜ steep git:(master) ✗ bundle exec steep check -I test.rbi test.rb test.rb:40:44: ArgumentTypeMismatch: receiver=::Phone.class constructor, expected=::String, actual=::Integer (1)
という訳で、string型が求められてる場所にintegerを渡すと怒ってくれる。
忘れがちな設定
@dynamic コメント
class Person # @dynamic name, contacts attr_reader :name, :contacts
attr_readerなど、methodとして定義しないattributesを扱う場合、
# @dynamic attribute_name
で指定しなければならない。
これを忘れると、下記のようにmethod missingのエラーが出る。
➜ steep git:(master) ✗ bundle exec steep check -I test.rbi test.rb test.rb:1:0: MethodDefinitionMissing: module=::Person, method=name (class Person) test.rb:1:0: MethodDefinitionMissing: module=::Person, method=contacts (class Person)
所感
- 普通に自分の個人プロダクトでは採用して良いのではなかろうかというレベルに来てる。
- Railsくらいの規模のコードで問題なく使えるか検証したい
- 大きなライブラリも導入してくれると、コードを読むときに助かる気持ち