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)
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
def name "T::Enumerable[#{@type.name}]" end
def subtype_of_single?(other)
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 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 underlying_class
def underlying_class Enumerable end
def valid?(obj)
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 # Enumerators can be unbounded: see `[:foo, :bar].cycle` 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