Home > Enterprise >  How can I resolve sorbet error: "Use of undeclared variable"?
How can I resolve sorbet error: "Use of undeclared variable"?

Time:12-29

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_readers, 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.

  • Related