🎃

Sorbetで子クラス内の定数を動的に参照しようとして怒られる場合

2025/02/10に公開

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"

Sorbet Playground Link

ここでは、親クラスが子クラスで定義してある定数を知らないため、型エラーになります。

解決方法

直接定数を参照するのをやめ、メソッドを経由して静的に型を解決する方法を考えます。
この例の場合、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"

Sorbet Playground Link

kindを取得するメソッドを親クラスにself.kindとして定義し、abstractを書きます。
すると、子クラスではself.kindを定義しないと怒られるようになります。
また、overrideを書くことで、親クラスでabstractメソッドが定義されていることも保証できるようになります。

Discussion