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 recursively_valid?(obj)

@override Base
def recursively_valid?(obj)
  return false unless obj.is_a?(Enumerable)
  case obj
  when Array
    begin
      it = 0
      while it < obj.count
        return false unless @type.recursively_valid?(obj[it])
        it += 1
      end
      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.recursively_valid?(v)
      return false if !key_type.recursively_valid?(key) || !value_type.recursively_valid?(val)
    end
    true
  when Enumerator
    # Enumerators can be unbounded: see `[:foo, :bar].cycle`
    true
  when Range
    # A nil beginning or a nil end does not provide any type information. That is, nil in a range represents
    # boundlessness, it does not express a type. For example `(nil...nil)` is not a T::Range[NilClass], its a range
    # of unknown types (T::Range[T.untyped]).
    # Similarly, `(nil...1)` is not a `T::Range[T.nilable(Integer)]`, it's a boundless range of Integer.
    (obj.begin.nil? || @type.recursively_valid?(obj.begin)) && (obj.end.nil? || @type.recursively_valid?(obj.end))
  when Set
    obj.each do |item|
      return false unless @type.recursively_valid?(item)
    end
    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

def subtype_of_single?(other)

@override Base
def subtype_of_single?(other)
er.class <= TypedEnumerable &&
erlying_class <= other.underlying_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
 can't get any information from `NilClass` in ranges (since nil is used to represent boundlessness).
able_objects = [obj.begin, obj.end].compact
ypeable_objects.empty?
:Range[T.untyped]

:Range[type_from_instances(typeable_objects)]
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
is is a specialized enumerable type, just return the class.
ct.instance_method(:class).bind(obj).call

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 underlying_class

def underlying_class
  Enumerable
end

def valid?(obj)

@override Base
def valid?(obj)
  obj.is_a?(Enumerable)
end