💡

Sorbetが親クラスやincludeしたmodule内で定義された定数を認識しない場合

2025/02/21に公開

SorbetでRubyに型チェックを導入すると、初期の段階でSorbetが継承周りの定数を解決できないという問題に当たります。
ここではその理由や解決方法についてメモしておきます。

問題

例えば、Parent内で定義した定数をChild経由で呼び出す場合や、module内で定義した定数includeしたクラス側経由で呼び出す場合に、Sorbetがエラーを出します。

class Parent
  X = 1
end

class Child < Parent
end

module Mixin
  Y = 2
end

class Foo
  include Mixin
end

p Child::X      # -> NG: Unable to resolve constant `X`
p Foo::Y        # -> NG: Unable to resolve constant `Y`

解決方法

解決方法はシンプルで、親クラスやincludeしたmodule内で定義された定数を認識しない場合は、直接定義されている場所を参照するようにしましょう。

class Parent
  X = 1
end

class Child < Parent
end

module Mixin
  Y = 2
end

class Foo
  include Mixin
end

p Parent::X     # -> OK
p Child::X      # -> NG: Unable to resolve constant `X`

p Mixin::Y      # -> OK
p Foo::Y        # -> NG: Unable to resolve constant `Y`

理由

Sorbetはクラスの継承によってアクセスできる定数の解決をサポートしてません。
これは、SorbetのサポートしてないRubyの機能のページに書いてあります。

理由としては、型チェックのパフォーマンスの担保と理解しやすさとのことです。
確かに、継承をずっと追っていって定数を見つけるのは少し大変そうです。
includeについては上記のページに明示されていませんが、同じような理由で解決できないと思われます。

ちなみに、実際には解決は可能だがSorbetの思想上、解決しないようにしていると思われます。
というのも、Child::XにホバーするとParent::Xへの修正を提案してくれるため、Sorbet自身はXを見つけられていることになります。

手元で確認したければ、以下をご参照ください。
Sorbet Playground Link

Discussion