I wanted to start using Steep for one of my projects, but I keep getting errors that I don't know how to fix. This is one example extracted from my code that I am struggling with. I removed all the complexity except for the relevant bits:
Setup
# lib/a/b.rb
module A
class B
C = [[''], ['2', '2\''], ['\'', '3', '’']].freeze
D = (['0'] + C.map(&:first)).freeze
def initialize(v)
@v = v
end
def <=>(other)
@v <=> other.v
end
end
end
My signatures look like this:
# sig/a/b.rbs
module A
class B
C: ::Array[::Array[::String]]
D: ::Array[::String]
@v: ::Integer
def initialize: (::Integer v) -> untyped
attr_reader v: ::Integer
def <=>: (self other) -> ::Integer
end
end
Steepfile:
target :lib do
signature 'sig'
check 'lib'
end
Steep now says:
lib/a/b.rb:4:4: IncompatibleAssignment: lhs_type=::Array[::String], rhs_type=::Array[(::String | nil)] (D = (['0'] + C.map(&:first)).freeze)
::Array[(::String | nil)] <: ::Array[::String]
(::String | nil) <: ::String
nil <: ::String
==> nil <: ::String does not hold
lib/a/b.rb:10:4: MethodBodyTypeMismatch: method=<=>, expected=::Integer, actual=(::Integer | nil) (def <=>(other))
(::Integer | nil) <: ::Integer
nil <: ::Integer
==> nil <: ::Integer does not hold
Problem
I think I understand the complaint. In general Array#first
returns either a value from that array or nil
. However, my Array was just defined one line above and so I know that it's not nil
in this particular case.
Same thing for <=>
. It could return nil
in some cases, but given that I typed the argument other
as self
, I know other.value
is an Integer
and a <=> b
where a
and b
are integers returns an integer. I.e. I know that it's not nil
here.
But how do I tell steep that? Can I cast this to non-nil
somehow?