I have code that looks like this (playground link):
# typed: strict
class A
extend T::Sig
sig { returns(T::Array[Integer]) }
def compute_expensive
[1, 2, 3]
end
sig { returns(T::Array[Integer]) }
def expensive
@expensive ||= T.let(compute_expensive, T::Array[Integer])
end
end
This fails to typecheck, saying that:
editor.rb:12: The instance variable @expensive must be declared inside initialize or declared nilable https://srb.help/5005
12 | @expensive ||= T.let(compute_expensive, Integer)
^^^^^^^^^^
I've tried a couple things to get around this…
- When I declare the type as
T.nilable(Integer)
, Sorbet says that the return type does not match the sig. Fair. - When I declare the type in
initialize
as@expensive = nil
, Sorbet says thatnil
does not type check with theInteger
definition below. Also fair. - If I declare
@expensive = []
ininitialize
, my assignment with||=
becomes unreachable. - I can of course say
@expensive = compute_expensive if @expensive.empty?
and then return@expensive
but I'm more interested in how Sorbet's type system can accommodate the||=
pattern.
This feels like a really common pattern in Ruby to me! How can I get Sorbet to type-check it for me?