🎃
[Sorbet] mixes_in_class_methodsでmoduleがクラスに生やしたメソッドを認識させる
Rubyでは、あるmoduleがクラスからincludeされた場合に、クラス側にmodule側で定義したメソッドを生やすというパターンがあります。
今回は、Sorbetのmixes_in_class_methods
という機能を使って、メソッドの存在を型の上で知らせる方法について紹介します。
問題
以下のようにmodule Mでfooというメソッドを定義し、includeしたクラスにfooメソッドを生やしているとします。
普通に実行すれば、p A.foo
で"foo"
という文字列が出力される正しいコードです。
module M
module ClassMethods
def foo = 'foo'
end
def self.included(other)
other.extend(ClassMethods)
end
end
class A
include M
end
p A.foo # Method foo does not exist on T.class_of(A)
しかし、Sorbetはmodule MがAにメソッドを生やしていることを知らないため、A.foo
にアクセスした行でMethod foo does not exist on T.class_of(A)
という型エラーを出します。
include Mをしたときに、module ClassMethods経由でメソッドを生やしていることを示さなければいけません。
解決方法
そこで、mixes_in_class_methods
使って、このmoduleをincludeしたクラスにClassMethods内で定義されたクラスメソッドを型として生やすようにします。
module M
extend T::Helpers
module ClassMethods
def foo = 'foo'
end
def self.included(other)
other.extend(ClassMethods)
end
mixes_in_class_methods(ClassMethods)
end
class A
include M
end
p A.foo # -> "foo"
mixes_in_class_methods(ClassMethods)
と書くことで、include MしているクラスAにfooというメソッドが生えたことを型で保証できます。
Sorbetの公式ドキュメントではmixes_in_class_methods
単体のページはありませんが、Abstract Classes and Interfacesのページで言及されています。
Discussion