class T::Types::FixedArray

in the list.
Takes a list of types. Validates each item in an array using the type in the same position

def build_type

def build_type
  types
  nil
end

def describe_obj(obj)

overrides Base

"Expected [String, Symbol], got Array".
instead of
"Expected [String, Symbol], got [String, String]"
This gives us better errors, e.g.:
def describe_obj(obj)
  if obj.is_a?(Array)
    if obj.length == types.length
      item_classes = obj.map(&:class).join(', ')
      "type [#{item_classes}]"
    else
      "array of size #{obj.length}"
    end
  else
    super
  end
end

def initialize(types)

def initialize(types)
  @inner_types = types
end

def name

overrides Base
def name
  "[#{types.join(', ')}]"
end

def recursively_valid?(obj)

overrides Base
def recursively_valid?(obj)
  if obj.is_a?(Array) && obj.length == types.length
    i = 0
    while i < types.length
      if !types[i].recursively_valid?(obj[i])
        return false
      end
      i += 1
    end
    true
  else
    false
  end
end

def subtype_of_single?(other)

overrides Base
def subtype_of_single?(other)
ther
ixedArray
operly speaking, covariance here is unsound since arrays
n be mutated, but sorbet implements covariant tuples for
se of adoption.
s.size == other.types.size && types.zip(other.types).all? do |t1, t2|
.subtype_of?(t2)
ypedArray
rning: covariant arrays
e1, value2, *values_rest = types
e_type = if !value2.nil?
:Types::Union::Private::Pool.union_of_types(value1, value2, values_rest)
f value1.nil?
untyped

lue1
ypes::TypedArray.new(value_type).subtype_of?(other)
e

def types

def types
  @types ||= @inner_types.map {|type| T::Utils.coerce(type)}
end

def valid?(obj)

overrides Base
def valid?(obj)
  if obj.is_a?(Array) && obj.length == types.length
    i = 0
    while i < types.length
      if !types[i].valid?(obj[i])
        return false
      end
      i += 1
    end
    true
  else
    false
  end
end