I am experimenting with adding sorbet type information to my gem, pdf-reader. I don't want sorbet to be a runtime dependency for the gem, so all type annotations are in an external file in the rbi/
directory. I also can't extend T::Sig
in my classes.
I'd like to enable typed: strict
in some files, but doing so flags that I'm using some instance variables without type annotations:
./lib/pdf/reader/rectangle.rb:94: Use of undeclared variable @bottom_left https://srb.help/6002
94 | @bottom_left = PDF::Reader::Point.new(
^^^^^^^^^^^^
./lib/pdf/reader/rectangle.rb:98: Use of undeclared variable @bottom_right https://srb.help/6002
98 | @bottom_right = PDF::Reader::Point.new(
^^^^^^^^^^^^^
./lib/pdf/reader/rectangle.rb:102: Use of undeclared variable @top_left https://srb.help/6002
102 | @top_left = PDF::Reader::Point.new(
^^^^^^^^^
./lib/pdf/reader/rectangle.rb:106: Use of undeclared variable @top_right https://srb.help/6002
106 | @top_right = PDF::Reader::Point.new(
The proposed fix is to use T.let()
:
@top_right = T.let(PDF::Reader::Point.new(0,0), PDF::Reader::Point)
However I can't do that because it requires a runtime dependency on sorbet.
Is it possible to record annotations for instance variables in an rbi file?
CodePudding user response:
According to the documentation "The syntax of RBI files is the same as normal Ruby files, except that method definitions do not need implementations." So, the syntax for declaring the type of an instance variable in an RBI file is the same as in a Ruby file:
sig do
params(
x1: Numeric,
y1: Numeric,
x2: Numeric,
y2: Numeric
).void
end
def initialize(x1, y1, x2, y2)
@top_right = T.let(PDF::Reader::Point.new(0,0), PDF::Reader::Point)
# …
end
An alternative would be to use RBS syntax instead of RBI syntax, which supports type annotations for instance variables natively. However, I have found conflicting information on RBS support in Sorbet. There are claims online that Sorbet supports RBS. OTOH, the Sorbet FAQ talks about RBS support in the future tense. On the other other hand, the "future" the FAQ talks about is the release of Ruby 3, which is actually one year in the past.
In RBS, it would look something like this:
module PDF
class Reader
@top_left: Numeric
@top_right: Numeric
@bottom_left: Numeric
@bottom_right: Numeric
class Rectangle
def initialize: (
x1: Numeric,
y1: Numeric,
x2: Numeric,
y2: Numeric
) -> void
end
end
end
Or, since they are also attr_reader
s, it might be enough to just do
module PDF
class Reader
attr_reader top_left: Numeric
attr_reader top_right: Numeric
attr_reader bottom_left: Numeric
attr_reader bottom_right: Numeric
class Rectangle
def initialize: (
x1: Numeric,
y1: Numeric,
x2: Numeric,
y2: Numeric
) -> void
end
end
end
I believe, this will also implicitly type the corresponding instance variables, but I have not tested this.