🎃
Sorbetで子クラス内の定数を動的に参照しようとして怒られる場合
RubyのSorbetで型チェック行っている際、
「Dynamic constant references are unsupported. (5001)」
と怒られる場合の1ケースについて対応してみます。
問題
親クラスに共通の処理が書いてあって、子クラスで定数を定義しているコードがあるとします。
class Parent
extend T::Sig
extend T::Helpers
def show_kind = self.class::KIND # "Dynamic constant references are unsupported"
end
class ChildA < Parent
KIND = 'A'
end
class ChildB < Parent
KIND = 'B'
end
p ChildA.new.show_kind # -> "A"
p ChildB.new.show_kind # -> "B"
ここでは、親クラスが子クラスで定義してある定数を知らないため、型エラーになります。
解決方法
直接定数を参照するのをやめ、メソッドを経由して静的に型を解決する方法を考えます。
この例の場合、abstractメソッドを使うとシンプルに解決できます。
class Parent
extend T::Sig
extend T::Helpers
abstract!
sig { abstract.returns(String) }
def self.kind; end
def show_kind = self.class.kind
end
class ChildA < Parent
KIND = 'A'
sig { override.returns(String) }
def self.kind = KIND
end
class ChildB < Parent
KIND = 'B'
sig { override.returns(String) }
def self.kind = KIND
end
p ChildA.new.show_kind # -> "A"
p ChildB.new.show_kind # -> "B"
kindを取得するメソッドを親クラスにself.kindとして定義し、abstractを書きます。
すると、子クラスではself.kindを定義しないと怒られるようになります。
また、overrideを書くことで、親クラスでabstractメソッドが定義されていることも保証できるようになります。
Discussion