Techioz Blog

T::Enum は、型に使用された無効な列挙型のエラーを発生させる代わりに、無効な列挙型が使用された場合に初期化されていない定数を発生させます

概要

私たちのアプリでは、ボタン コンポーネントのバリアントに Enum を使用します。

class ButtonComponent
  sig do
    params(
      button_variant: ButtonVariant
    ).void
  end
  def initialize(button_variant: ButtonVariant::Primary)
end

列挙型は次のとおりです。

class ButtonComponent
  class ButtonVariant < T::Enum
    extend T::Sig

    enums do
      Primary = new
      Secondary = new
      Tertiary = new
    end

    sig { returns(String) }
    def css_class
      case self
      when Primary
        'btn--primary'
      when Secondary
        'btn--secondary'
      when Tertiary
        'btn--tertiary'
      else
        T.absurd self
      end
    end
  end
end

ここで、無効なオブジェクトを渡すと次のようになります。

render ButtonComponent.new(button_variant: ButtonComponent::UNKNOWN)

Sorbet からは、間違った型が渡されたという素晴らしいエラーが返されます。

Parameter 'button_variant': Expected type ButtonComponent::ButtonVariant

ただし、正しい型を渡しても無効な列挙型を渡した場合は、次のようになります。

render ButtonComponent.new(button_variant: ButtonComponent::ButtonVariant::UNKNOWN)

定数が不明であるというエラーのみが表示されます。

uninitialized constant ButtonComponent::ButtonVariant::UNKNOWN

Sorbet は、初期化されていない定数に関するより一般的なエラーをスローするのではなく、列挙型が無効であるというエラーを生成するべきではないでしょうか?

私たちは、開発者にとって何が問題なのかが明らかになるように、エラーがより明確になることを期待していました。 ButtonComponent::ButtonVariant 型に無効な ‘enum’ が使用されました。列挙型を間違って使用したのでしょうか、それともこれは Sorbet で予期された動作なのでしょうか?

解決策

この小さな例は次のとおりです (sorbet.run を参照)。

# typed: true
class Foo < T::Enum
  enums do
    BAR = new('bar')
  end
end

Foo::BAR
Foo::Zoo
#    ^ Unable to resolve constant Zoo https://srb.help/5002

2023 年 9 月 11 日の時点では、Sorbet はこれを未解決の定数と見なしているため、これは予想される動作です。

sorbet の GitHub で問題を報告すると、修正が検討されるか、修正できない理由についてのより詳細な説明が得られます。