Search code examples
rubysorbet

Sorbet - concrete type when inheriting from generic class


How can I specify String for the generic type?

→ View on sorbet.run

# typed: strict


class NameList < Array
  extend T::Sig

  sig {params(names: T::Array[String]).void}
  def initialize(names)
    names.each { |n| self << n}
  end

  sig {returns(String)}
  def csv
    (map { |n| n.join(',') }).join("\n")
  end
end


nl = NameList.new(['Bruce', 'Clark', 'Peter'])

# Output length of first name.
puts(nl.first&.size)  # String#size
puts(nl.first&.sizee)  # misspelled size - should be an error

# Sorbet-Static output:
# editor.rb:4: Type Elem declared by parent Array must be re-declared in NameList https://srb.help/5036
#      4 |class NameList < Array
#         ^^^^^^^^^^^^^^^^^^^^^^
#     https://github.com/sorbet/sorbet/tree/master/rbi/core/array.rbi#L350: Elem declared in parent here
#      350 |  Elem = type_member(:out)
#             ^^^^^^^^^^^^^^^^^^^^^^^^
# Errors: 1

Solution

  • You can use

    Elem = type_member(fixed: String)
    

    Re-declaring the parent's type member is already obligatory (which you can see in the error message on your code snippet, which says Type Elem declared by parent Array must be re-declared in NameList) and re-declaring it with the fixed: parameter allows that type parameter to be specified as a concrete type.

    → View on sorbet.run

    # typed: strict
    
    class NameList < Array
      extend T::Sig
    
      Elem = type_member(fixed: String)
    
      sig {params(names: T::Array[String]).void}
      def initialize(names)
        names.each { |n| self << n}
      end
    
      sig {returns(String)}
      def csv
        (map { |n| n.join(',') }).join("\n")  # Method `join` does not exist on String
      end
    end
    
    
    nl = NameList.new(['Bruce', 'Clark', 'Peter'])
    
    # Output length of first name.
    puts(nl.first&.size)
    puts(nl.first&.sizee)  # Method `sizee` does not exist on String