rubyenumssorbet

T::Enum raises uninitialized constant if invalid enum used instead of raising error of invalid enum used for type


In our app we use an Enum for variant on our button component:

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

The enum is as follows:

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

Now if we pass in an invalid object:

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

we get a nice error from Sorbet about the incorrect type being passed:

Parameter 'button_variant': Expected type ButtonComponent::ButtonVariant

However if we pass in the correct type, but an invalid enum:

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

We get just an error that the constant is unknown:

uninitialized constant ButtonComponent::ButtonVariant::UNKNOWN

Shouldn't Sorbet be raising an error that the Enum is invalid rather than throwing a much more generic error about uninitialized constants?

We were expecting the errors would be much more explicit so its clear to the developer what's wrong e.g. invalid 'enum' used for type ButtonComponent::ButtonVariant. Have we use the enum incorrectly or is this expected behaviour in Sorbet?


Solution

  • A smaller example of this is the following (see in 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
    

    As of 2023-09-11, Sorbet considers this an unresolved constant, so this is the expected behavior.

    You can report the issue in sorbet's GitHub, and it will either be considered for a fix, or you'll get a more detailed explanation on why this is not fixable.