class T::Types::TypedEnumerable

error messages.
‘case` statement below in `describe_obj` in order to get better
Note: All subclasses of Enumerable should add themselves to the

def describe_obj(obj)

@override Base
def describe_obj(obj)
  return super unless obj.is_a?(Enumerable)
  type_from_instance(obj).name
end

def initialize(type)

def initialize(type)
  @type = T::Utils.coerce(type)
end

def name

@override Base
def name
  "T::Enumerable[#{@type.name}]"
end

def subtype_of_single?(other)

@override Base
def subtype_of_single?(other)
f.class <= other.class
umerables are covariant because they are read only
operly speaking, many Enumerable subtypes (e.g. Set)
ould be invariant because they are mutable and support
th reading and writing. However, Sorbet treats *all*
umerable subclasses as covariant for ease of adoption.
e.subtype_of?(other.type)
e

def type_from_instance(obj)

def type_from_instance(obj)
ue, false].include?(obj)
rn T::Boolean
!obj.is_a?(Enumerable)
rn obj.class
bj
rray
rray[type_from_instances(obj)]
ash
rred_key = type_from_instances(obj.keys)
rred_val = type_from_instances(obj.values)
ash[inferred_key, inferred_val]
ange
ange[type_from_instances([obj.first, obj.last])]
numerator
numerator[type_from_instances(obj)]
et
et[type_from_instances(obj)]
O
ort circuit for anything IO-like (File, etc.). In these cases,
umerating the object is a destructive operation and might hang.
class
.class.new(type_from_instances(obj))

def type_from_instances(objs)

def type_from_instances(objs)
 objs.class unless objs.is_a?(Enumerable)
ed_types = []
.each do |x|
tained_types << type_from_instance(x)

rn T.untyped # all we can do is go with the types we have so far
ained_types.count > 1
ltiple kinds of bad types showed up, we'll suggest a union
pe you might want.
n.new(obtained_types)
obtained_types.empty?
return
erything was the same bad type, lets just show that
ined_types.first

def valid?(obj)

@override Base
def valid?(obj)
  return false unless obj.is_a?(Enumerable)
  case obj
  when Array
    begin
      it = 0
      while it < obj.count
        return false unless @type.valid?(obj[it])
        it += 1
      end
      return true
    end
  when Hash
    return false unless @type.is_a?(FixedArray)
    types = @type.types
    return false if types.count != 2
    key_type = types[0]
    value_type = types[1]
    obj.each_pair do |key, val|
      # Some objects (I'm looking at you Rack::Utils::HeaderHash) don't
      # iterate over a [key, value] array, so we can't juse use the @type.valid?(v)
      return false if !key_type.valid?(key) || !value_type.valid?(val)
    end
    return true
  when Enumerator::Lazy
    # Users don't want these walked
    return true
  when Enumerator
    obj.each do |elem|
      return false unless @type.valid?(elem)
    end
    return true
  when Range
    @type.valid?(obj.first) && @type.valid?(obj.last)
  when Set
    obj.each do |item|
      return false unless @type.valid?(item)
    end
    return true
  else
    # We don't check the enumerable since it isn't guaranteed to be
    # rewindable (e.g. STDIN) and it may be expensive to enumerate
    # (e.g. an enumerator that makes an HTTP request)"
    true
  end
end